@sumaris-net/ngx-components 21.4.2 → 21.4.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.
- package/AGENTS.md +26 -15
- package/doc/changelog.md +21 -1
- package/fesm2022/sumaris-net.ngx-components.mjs +872 -62
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/assets/i18n/en-US.json +22 -1
- package/src/assets/i18n/en.json +22 -2
- package/src/assets/i18n/fr.json +22 -2
- package/src/assets/manifest.json +1 -1
- package/types/sumaris-net.ngx-components.d.ts +288 -47
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Directive, inject, Pipe, Injectable, EventEmitter, Output, Optional, Inject, NgModule, forwardRef, ElementRef, booleanAttribute, ViewChildren, Input, ChangeDetectionStrategy, Component, HostListener, HostBinding, numberAttribute, ViewChild, isDevMode, ANIMATION_MODULE_TYPE, RendererStyleFlags2, DOCUMENT, TemplateRef, ContentChild, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Self, ViewEncapsulation, Injector, provideAppInitializer, APP_INITIALIZER, DestroyRef, input, output, Renderer2, effect,
|
|
3
|
-
import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, noop as noop$a, Observable, forkJoin, timeout, defer, combineLatest, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, mergeMap as mergeMap$1, switchMap as switchMap$1,
|
|
2
|
+
import { InjectionToken, Directive, inject, Pipe, Injectable, EventEmitter, Output, Optional, Inject, NgModule, forwardRef, ElementRef, booleanAttribute, ViewChildren, Input, ChangeDetectionStrategy, Component, HostListener, HostBinding, numberAttribute, ViewChild, isDevMode, ANIMATION_MODULE_TYPE, RendererStyleFlags2, DOCUMENT, TemplateRef, ContentChild, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Self, ViewEncapsulation, viewChild, signal, computed, Injector, provideAppInitializer, APP_INITIALIZER, DestroyRef, input, output, Renderer2, effect, SecurityContext, EnvironmentInjector, runInInjectionContext, viewChildren, model, untracked } from '@angular/core';
|
|
3
|
+
import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, EMPTY, noop as noop$a, Observable, forkJoin, timeout, defer, combineLatest, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, mergeMap as mergeMap$1, switchMap as switchMap$1, interval } from 'rxjs';
|
|
4
4
|
import { catchError, map, filter, takeUntil, first, switchMap, tap as tap$1, throttleTime, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, finalize, distinctUntilKeyChanged, take } from 'rxjs/operators';
|
|
5
5
|
import * as cloneImported from 'clone';
|
|
6
6
|
import * as i2$3 from '@angular/common';
|
|
7
|
-
import { CommonModule, APP_BASE_HREF, LocationStrategy,
|
|
7
|
+
import { CommonModule, Location, APP_BASE_HREF, LocationStrategy, NgIf, AsyncPipe, JsonPipe } from '@angular/common';
|
|
8
8
|
import { CdkTableModule } from '@angular/cdk/table';
|
|
9
9
|
import * as i1$4 from '@angular/cdk/a11y';
|
|
10
10
|
import { A11yModule, FocusMonitor } from '@angular/cdk/a11y';
|
|
@@ -109,9 +109,9 @@ import { base64Decode, cryptoWaitReady, ed25519PairFromSeed, ed25519Sign, base64
|
|
|
109
109
|
import * as i4$5 from '@angular/cdk/drag-drop';
|
|
110
110
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
111
111
|
import * as i4$3 from 'ngx-markdown';
|
|
112
|
-
import { MarkdownModule, MarkedRenderer, MARKED_OPTIONS, SANITIZE } from 'ngx-markdown';
|
|
112
|
+
import { MarkdownModule, MarkedRenderer, MARKED_OPTIONS, MARKED_EXTENSIONS, SANITIZE } from 'ngx-markdown';
|
|
113
113
|
import * as i1$5 from '@angular/router';
|
|
114
|
-
import {
|
|
114
|
+
import { ActivatedRoute, Router, RouterModule, RouteReuseStrategy, NavigationEnd } from '@angular/router';
|
|
115
115
|
import { Drivers } from '@ionic/storage';
|
|
116
116
|
import * as LocalForage from 'localforage';
|
|
117
117
|
import * as CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
|
|
@@ -121,7 +121,7 @@ import * as i4$4 from 'ngx-jdenticon';
|
|
|
121
121
|
import { NgxJdenticonModule } from 'ngx-jdenticon';
|
|
122
122
|
import { __decorate, __param } from 'tslib';
|
|
123
123
|
import { Apollo, gql as gql$1, provideApollo } from 'apollo-angular';
|
|
124
|
-
import { ApolloLink, NetworkStatus, gql, InMemoryCache } from '@apollo/client/core';
|
|
124
|
+
import { ApolloLink, NetworkStatus, gql, InMemoryCache, DocumentTransform } from '@apollo/client/core';
|
|
125
125
|
import { trigger, transition, style, animate, state } from '@angular/animations';
|
|
126
126
|
import { Network } from '@capacitor/network';
|
|
127
127
|
import * as i3$6 from '@rx-angular/state';
|
|
@@ -130,6 +130,7 @@ import { SplashScreen } from '@capacitor/splash-screen';
|
|
|
130
130
|
import * as i4$2 from 'ionic-cache';
|
|
131
131
|
import { CacheModule } from 'ionic-cache';
|
|
132
132
|
import { getMainDefinition } from '@apollo/client/utilities';
|
|
133
|
+
import { visit, Kind } from 'graphql';
|
|
133
134
|
import { v4 } from 'uuid';
|
|
134
135
|
import * as LoggerLinkModule from 'apollo-link-logger';
|
|
135
136
|
import { scryptEncode } from '@polkadot/util-crypto/scrypt/encode';
|
|
@@ -155,6 +156,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
155
156
|
import { Geolocation } from '@capacitor/geolocation';
|
|
156
157
|
import * as i3$5 from '@e-is/ngx-material-table';
|
|
157
158
|
import { ValidatorService, TableDataSource, AsyncTableDataSource } from '@e-is/ngx-material-table';
|
|
159
|
+
import { HttpLink as HttpLink$1 } from '@apollo/client/link/http';
|
|
160
|
+
import { Defer20220824Handler } from '@apollo/client/incremental';
|
|
158
161
|
import { persistCacheSync } from 'apollo4-cache-persist';
|
|
159
162
|
import { HttpLink } from 'apollo-angular/http';
|
|
160
163
|
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
|
|
@@ -163,7 +166,6 @@ import { RetryLink } from '@apollo/client/link/retry';
|
|
|
163
166
|
import { createFragmentRegistry } from '@apollo/client/cache';
|
|
164
167
|
import { createOperation } from '@apollo/client/link/utils';
|
|
165
168
|
import { checkDocument, getOperationDefinition } from '@apollo/client/utilities/internal';
|
|
166
|
-
import { Kind } from 'graphql';
|
|
167
169
|
import 'moment-timezone';
|
|
168
170
|
|
|
169
171
|
const ENVIRONMENT = new InjectionToken('ENV');
|
|
@@ -203,6 +205,10 @@ class Environment {
|
|
|
203
205
|
offline;
|
|
204
206
|
// Apollo (graphQL) config
|
|
205
207
|
apolloFetchPolicy;
|
|
208
|
+
// Enable the GraphQL @defer directive (incremental delivery) - default: false
|
|
209
|
+
// - true: queries with @defer are sent with a fetch link, and the response is streamed (multipart/mixed). Requires a pod that supports @defer.
|
|
210
|
+
// - false: @defer directives are removed from queries (all fields are fetched in a single response)
|
|
211
|
+
enableGraphqlDefer;
|
|
206
212
|
// Default values
|
|
207
213
|
defaultLocale;
|
|
208
214
|
defaultLatLongFormat;
|
|
@@ -7997,29 +8003,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
7997
8003
|
args: ['delete']
|
|
7998
8004
|
}] } });
|
|
7999
8005
|
|
|
8006
|
+
/**
|
|
8007
|
+
* Directive to automatically show a tooltip with the content of the label.
|
|
8008
|
+
*/
|
|
8000
8009
|
class AutoTooltipDirective {
|
|
8001
8010
|
elementRef;
|
|
8002
8011
|
matTooltip;
|
|
8012
|
+
/**
|
|
8013
|
+
* Enable automatic tooltip based on the content of the label.
|
|
8014
|
+
*/
|
|
8003
8015
|
appAutoTooltip = true;
|
|
8016
|
+
/**
|
|
8017
|
+
* Text to display in the tooltip when appAutoTooltip is false.
|
|
8018
|
+
*/
|
|
8019
|
+
appTooltipText = '';
|
|
8004
8020
|
constructor(elementRef, matTooltip) {
|
|
8005
8021
|
this.elementRef = elementRef;
|
|
8006
8022
|
this.matTooltip = matTooltip;
|
|
8007
8023
|
}
|
|
8008
8024
|
ngAfterContentChecked() {
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
this.matTooltip.message
|
|
8025
|
+
if (this.appAutoTooltip) {
|
|
8026
|
+
// Update Material Tooltip if different
|
|
8027
|
+
if (this.elementRef.nativeElement && this.matTooltip && this.matTooltip.message !== this.elementRef.nativeElement.textContent) {
|
|
8028
|
+
this.matTooltip.message = this.elementRef.nativeElement.textContent;
|
|
8029
|
+
}
|
|
8030
|
+
}
|
|
8031
|
+
else if (isNotNilOrBlank(this.appTooltipText)) {
|
|
8032
|
+
this.matTooltip.message = this.appTooltipText;
|
|
8012
8033
|
}
|
|
8013
8034
|
}
|
|
8014
8035
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: AutoTooltipDirective, deps: [{ token: ElementRef }, { token: i6.MatTooltip }], target: i0.ɵɵFactoryTarget.Directive });
|
|
8015
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.2.22", type: AutoTooltipDirective, isStandalone: false, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: { appAutoTooltip: ["appAutoTooltip", "appAutoTooltip", booleanAttribute] }, hostDirectives: [{ directive: i6.MatTooltip }], ngImport: i0 });
|
|
8036
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.2.22", type: AutoTooltipDirective, isStandalone: false, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: { appAutoTooltip: ["appAutoTooltip", "appAutoTooltip", booleanAttribute], appTooltipText: "appTooltipText" }, hostDirectives: [{ directive: i6.MatTooltip }], ngImport: i0 });
|
|
8016
8037
|
}
|
|
8017
8038
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: AutoTooltipDirective, decorators: [{
|
|
8018
8039
|
type: Directive,
|
|
8019
8040
|
args: [{
|
|
8020
8041
|
selector: 'ion-label[appAutoTooltip], mat-label[appAutoTooltip]',
|
|
8021
8042
|
hostDirectives: [MatTooltip],
|
|
8022
|
-
standalone: false
|
|
8043
|
+
standalone: false,
|
|
8023
8044
|
}]
|
|
8024
8045
|
}], ctorParameters: () => [{ type: i0.ElementRef, decorators: [{
|
|
8025
8046
|
type: Inject,
|
|
@@ -8027,6 +8048,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
8027
8048
|
}] }, { type: i6.MatTooltip }], propDecorators: { appAutoTooltip: [{
|
|
8028
8049
|
type: Input,
|
|
8029
8050
|
args: [{ transform: booleanAttribute }]
|
|
8051
|
+
}], appTooltipText: [{
|
|
8052
|
+
type: Input
|
|
8030
8053
|
}] } });
|
|
8031
8054
|
|
|
8032
8055
|
class SharedDirectivesModule {
|
|
@@ -10878,8 +10901,9 @@ class MatAutocompleteField {
|
|
|
10878
10901
|
this._openedSubscription.unsubscribe();
|
|
10879
10902
|
this._subscription.remove(this._openedSubscription);
|
|
10880
10903
|
}
|
|
10881
|
-
|
|
10882
|
-
const
|
|
10904
|
+
// Listen every opening: the 'more items' option (that calls this function) is not recreated when the panel reopens
|
|
10905
|
+
const opened$ = merge(this.autocomplete._isOpen ? timer(0) : EMPTY, this.autocomplete.opened.pipe(debounceTime(200)));
|
|
10906
|
+
const scrollEnd$ = opened$.pipe(map(() => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold).pipe(takeUntil(this.autocomplete.closed))));
|
|
10883
10907
|
// Trigger fetch more, when end scroll reached
|
|
10884
10908
|
this._openedSubscription = scrollEnd$.subscribe(() => this._fetchMore$.emit());
|
|
10885
10909
|
// Register subscription
|
|
@@ -10894,8 +10918,9 @@ class MatAutocompleteField {
|
|
|
10894
10918
|
this._openedSubscription.unsubscribe();
|
|
10895
10919
|
this._subscription.remove(this._openedSubscription);
|
|
10896
10920
|
}
|
|
10897
|
-
|
|
10898
|
-
const
|
|
10921
|
+
// Listen every opening: the 'more items' option (that calls this function) is not recreated when the panel reopens
|
|
10922
|
+
const opened$ = merge(this.matSelect.panelOpen ? timer(0) : EMPTY, this.matSelect._openedStream.pipe(debounceTime(200)));
|
|
10923
|
+
const scrollEnd$ = opened$.pipe(map(() => this.getMatSelectPanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold).pipe(takeUntil(this.matSelect._closedStream))));
|
|
10899
10924
|
// Trigger fetch more, end end scroll reached
|
|
10900
10925
|
this._openedSubscription = scrollEnd$.pipe(filter(() => this._moreItemsCount > 0)).subscribe(() => this._fetchMore$.emit());
|
|
10901
10926
|
// Register subscription
|
|
@@ -14301,8 +14326,9 @@ class MatChipsField {
|
|
|
14301
14326
|
this._openedSubscription.unsubscribe();
|
|
14302
14327
|
this._subscription.remove(this._openedSubscription);
|
|
14303
14328
|
}
|
|
14304
|
-
|
|
14305
|
-
const
|
|
14329
|
+
// Listen every opening: the 'more items' option (that calls this function) is not recreated when the panel reopens
|
|
14330
|
+
const opened$ = merge(this.autocomplete._isOpen ? timer(0) : EMPTY, this.autocomplete.opened.pipe(debounceTime(200)));
|
|
14331
|
+
const scrollEnd$ = opened$.pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold).pipe(takeUntil(this.autocomplete.closed))));
|
|
14306
14332
|
// Trigger fetch more, end end scroll reached
|
|
14307
14333
|
this._openedSubscription = scrollEnd$.subscribe(() => this._fetchMore$.emit());
|
|
14308
14334
|
// Register subscription
|
|
@@ -17660,11 +17686,11 @@ class UploadFileComponent {
|
|
|
17660
17686
|
return waitFor(() => this.processingFilesCount === 0, opts);
|
|
17661
17687
|
}
|
|
17662
17688
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: UploadFileComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i2.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
|
|
17663
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: UploadFileComponent, isStandalone: false, selector: "app-upload-file", inputs: { fileExtension: "fileExtension", fileExtensionsExcluded: "fileExtensionsExcluded", uniqueFile: "uniqueFile", instantUpload: "instantUpload", uploadFn: "uploadFn", deleteFn: "deleteFn", maxParallelUpload: "maxParallelUpload", maxFileSizeMb: "maxFileSizeMb", autoHideDropArea: "autoHideDropArea", showDropArea: "showDropArea", showThumbnails: "showThumbnails" }, outputs: { filesChange: "filesChange" }, viewQueries: [{ propertyName: "fileDropEl", first: true, predicate: ["fileDropRef"], descendants: true }], ngImport: i0, template: "@if (!showDropArea) {\n <!-- hidden file input (no drop area): used by browse() -->\n <input\n type=\"file\"\n #fileDropRef\n class=\"hidden-file-input\"\n hidden\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n} @else if ((!uniqueFile && !autoHideDropArea) || files.length === 0) {\n <div class=\"container\" appDragAndDrop (fileDropped)=\"onFileDropped($event)\">\n <input\n type=\"file\"\n #fileDropRef\n id=\"fileDropRef\"\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n <mat-icon style=\"font-size: xx-large\" color=\"accent\" class=\"text-size\">upload</mat-icon>\n <ion-label>\n <span\n [innerHTML]=\"\n 'FILE.UPLOAD.DRAG_AND_DROP' | translate: { extension: !fileExtension ? '' : ' (' + fileExtension + ')' }\n \"\n ></span>\n </ion-label>\n <ion-button color=\"tertiary\">{{ 'FILE.UPLOAD.BROWSE' | translate }}</ion-button>\n </div>\n}\n<!-- global progression (useful when many files are selected) -->\n@if (uploading && files.length > 1) {\n <div class=\"upload-summary\">\n <ion-label color=\"medium\">\n <small>{{ 'FILE.UPLOAD.DONE_COUNT' | translate: { count: doneFilesCount, total: files.length } }}</small>\n </ion-label>\n <ion-progress-bar [value]=\"doneFilesCount / files.length\" color=\"accent\"></ion-progress-bar>\n </div>\n}\n<div class=\"files-list\">\n <ion-list>\n @for (file of files; track file; let i = $index) {\n <ion-item>\n @if (showThumbnails && file.kind === 'image') {\n <!-- image thumbnail, or a skeleton until the image is ready -->\n <div slot=\"start\" class=\"file-thumbnail\">\n @if (file.error) {\n <mat-icon color=\"danger\">broken_image</mat-icon>\n } @else if (file.progress === 1 && file.response?.body) {\n <img [src]=\"file.response.body\" alt=\"\" />\n } @else {\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n }\n </div>\n } @else {\n <!-- icon of the file type (pdf, spreadsheet, document...) -->\n <div slot=\"start\" class=\"file-icon\" [class.file-thumbnail]=\"showThumbnails\" [attr.data-kind]=\"file.kind\">\n <mat-icon>{{ fileKindIcons[file.kind || 'other'] }}</mat-icon>\n </div>\n }\n <div class=\"single-file\">\n <ion-label>\n <h4 class=\"name\" [class.deleting]=\"file.deleting\">\n {{ file.name }}\n </h4>\n <p>\n {{ file.size | fileSize }}\n </p>\n </ion-label>\n @if (uploading && !file.error && !showThumbnails) {\n <ion-progress-bar\n [value]=\"file.progress || 0\"\n [type]=\"file.deleting || file.progress === -1 ? 'indeterminate' : 'determinate'\"\n ></ion-progress-bar>\n }\n @if (file.error) {\n <ion-text\n color=\"danger\"\n [innerHTML]=\"\n file.error\n | translate\n : {\n fileExtension: fileExtension,\n fileExtensionsExcluded: fileExtensionsExcluded.join(', '),\n maxFileSizeMb: maxFileSizeMb,\n }\n \"\n ></ion-text>\n }\n </div>\n <!-- remote from list (before importation) -->\n @if (!file.progress || file.error) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n <!-- delete file (remotely - after importation) -->\n @if (file.progress === 1 && !!deleteFn && !file.deleting) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n </ion-item>\n }\n </ion-list>\n</div>\n", styles: [".container{width:calc(100% - 3rem);min-height:200px;padding:2rem;text-align:center;border:dashed 1px #979797;position:relative;margin:1.5rem}.container input{opacity:0;position:absolute;z-index:2;width:100%;height:100%;top:0;left:0;cursor:pointer}.container ion-label{display:block;font-size:20px;font-weight:600;color:#38424c}.container label{display:inline-block;color:#fff;width:183px;height:44px;border-radius:21.5px;background-color:#db202f;padding:8px 16px}.fileover{border:solid 1px var(--ion-color-accent)}.upload-summary{padding:0 1.5rem .5rem}.upload-summary ion-progress-bar{margin-top:4px}.file-thumbnail{--thumbnail-size: 48px;width:var(--thumbnail-size);height:var(--thumbnail-size);flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:4px;overflow:hidden}.file-thumbnail img{width:100%;height:100%;object-fit:cover}.file-thumbnail ion-skeleton-text{width:100%;height:100%;margin:0}.file-icon{display:flex;align-items:center;justify-content:center;color:var(--ion-color-medium)}.file-icon.file-thumbnail mat-icon{--icon-size: 36px;font-size:var(--icon-size);width:var(--icon-size);height:var(--icon-size)}.file-icon[data-kind=pdf]{color:#d93025}.file-icon[data-kind=document]{color:#2b579a}.file-icon[data-kind=spreadsheet]{color:#217346}.file-icon[data-kind=presentation]{color:#d24726}.file-icon[data-kind=archive]{color:#b8860b}.file-icon[data-kind=image]{color:var(--ion-color-tertiary)}.file-icon[data-kind=video],.file-icon[data-kind=audio]{color:#7b1fa2}.files-list{max-height:300px;overflow:auto}.files-list .single-file{display:flex;flex-direction:column}.files-list .single-file .name{word-break:break-all}.files-list .single-file .name.deleting{color:gray!important;font-style:italic!important}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$2.IonProgressBar, selector: "ion-progress-bar", inputs: ["buffer", "color", "mode", "reversed", "type", "value"] }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: DragAndDropDirective, selector: "[appDragAndDrop]", outputs: ["fileDropped"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }] });
|
|
17689
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: UploadFileComponent, isStandalone: false, selector: "app-upload-file", inputs: { fileExtension: "fileExtension", fileExtensionsExcluded: "fileExtensionsExcluded", uniqueFile: "uniqueFile", instantUpload: "instantUpload", uploadFn: "uploadFn", deleteFn: "deleteFn", maxParallelUpload: "maxParallelUpload", maxFileSizeMb: "maxFileSizeMb", autoHideDropArea: "autoHideDropArea", showDropArea: "showDropArea", showThumbnails: "showThumbnails" }, outputs: { filesChange: "filesChange" }, viewQueries: [{ propertyName: "fileDropEl", first: true, predicate: ["fileDropRef"], descendants: true }], ngImport: i0, template: "@if (!showDropArea) {\n <!-- hidden file input (no drop area): used by browse() -->\n <input\n type=\"file\"\n #fileDropRef\n class=\"hidden-file-input\"\n hidden\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n} @else if ((!uniqueFile && !autoHideDropArea) || files.length === 0) {\n <div class=\"container\" appDragAndDrop (fileDropped)=\"onFileDropped($event)\">\n <input\n type=\"file\"\n #fileDropRef\n id=\"fileDropRef\"\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n <mat-icon style=\"font-size: xx-large\" color=\"accent\" class=\"text-size\">upload</mat-icon>\n <ion-label>\n <span\n [innerHTML]=\"\n 'FILE.UPLOAD.DRAG_AND_DROP' | translate: { extension: !fileExtension ? '' : ' (' + fileExtension + ')' }\n \"\n ></span>\n </ion-label>\n <ion-button color=\"tertiary\">{{ 'FILE.UPLOAD.BROWSE' | translate }}</ion-button>\n </div>\n}\n<!-- global progression (useful when many files are selected) -->\n@if (uploading && files.length > 1) {\n <div class=\"upload-summary\">\n <ion-label color=\"medium\">\n <small>{{ 'FILE.UPLOAD.DONE_COUNT' | translate: { count: doneFilesCount, total: files.length } }}</small>\n </ion-label>\n <ion-progress-bar [value]=\"doneFilesCount / files.length\" color=\"accent\"></ion-progress-bar>\n </div>\n}\n<div class=\"files-list\">\n <ion-list>\n @for (file of files; track file; let i = $index) {\n <ion-item>\n @if (showThumbnails && file.kind === 'image') {\n <!-- image thumbnail, or a skeleton until the image is ready -->\n <div slot=\"start\" class=\"file-thumbnail\">\n @if (file.error) {\n <mat-icon color=\"danger\">broken_image</mat-icon>\n } @else if (file.progress === 1 && file.response?.body) {\n <img [src]=\"file.response.body\" alt=\"\" />\n } @else {\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n }\n </div>\n } @else {\n <!-- icon of the file type (pdf, spreadsheet, document...) -->\n <div slot=\"start\" class=\"file-icon\" [class.file-thumbnail]=\"showThumbnails\" [attr.data-kind]=\"file.kind\">\n <mat-icon>{{ fileKindIcons[file.kind || 'other'] }}</mat-icon>\n </div>\n }\n <div class=\"single-file\">\n <ion-label>\n <h4 class=\"name\" [class.deleting]=\"file.deleting\">\n {{ file.name }}\n </h4>\n <p>\n {{ file.size | fileSize }}\n </p>\n </ion-label>\n @if (uploading && !file.error && !showThumbnails) {\n <ion-progress-bar\n [value]=\"file.progress || 0\"\n [type]=\"file.deleting || file.progress === -1 ? 'indeterminate' : 'determinate'\"\n ></ion-progress-bar>\n }\n @if (file.error) {\n <ion-text\n color=\"danger\"\n [innerHTML]=\"\n file.error\n | translate\n : {\n fileExtension: fileExtension,\n fileExtensionsExcluded: fileExtensionsExcluded.join(', '),\n maxFileSizeMb: maxFileSizeMb,\n }\n \"\n ></ion-text>\n }\n </div>\n <!-- remote from list (before importation) -->\n @if (!file.progress || file.error) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n <!-- delete file (remotely - after importation) -->\n @if (file.progress === 1 && !!deleteFn && !file.deleting) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n </ion-item>\n }\n </ion-list>\n</div>\n", styles: [".container{width:calc(100% - 3rem);min-height:200px;padding:2rem;text-align:center;border:dashed 1px #979797;position:relative;margin:1.5rem}.container input{opacity:0;position:absolute;z-index:2;width:100%;height:100%;top:0;left:0;cursor:pointer}.container ion-label{display:block;font-size:20px;font-weight:600;color:#38424c}.container label{display:inline-block;color:#fff;width:183px;height:44px;border-radius:21.5px;background-color:#db202f;padding:8px 16px}.fileover{border:solid 1px var(--ion-color-accent)}.upload-summary{padding:0 1.5rem .5rem}.upload-summary ion-progress-bar{margin-top:4px}.file-thumbnail{--thumbnail-size: 48px;width:var(--thumbnail-size);height:var(--thumbnail-size);flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:4px;overflow:hidden}.file-thumbnail img{width:100%;height:100%;object-fit:cover}.file-thumbnail ion-skeleton-text{width:100%;height:100%;margin:0}.file-icon{display:flex;align-items:center;justify-content:center;color:var(--ion-color-medium)}.file-icon.file-thumbnail mat-icon{--icon-size: 36px;font-size:var(--icon-size);width:var(--icon-size);height:var(--icon-size)}.file-icon[data-kind=pdf]{color:#d93025}.file-icon[data-kind=document]{color:#2b579a}.file-icon[data-kind=spreadsheet]{color:#217346}.file-icon[data-kind=presentation]{color:#d24726}.file-icon[data-kind=archive]{color:#b8860b}.file-icon[data-kind=image]{color:var(--ion-color-tertiary)}.file-icon[data-kind=video],.file-icon[data-kind=audio]{color:#7b1fa2}.files-list{max-height:var(--app-upload-file-list-max-height, 300px);overflow:auto}.files-list .single-file{display:flex;flex-direction:column;flex:1 1 auto}.files-list .single-file .name{word-break:break-all}.files-list .single-file .name.deleting{color:gray!important;font-style:italic!important}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$2.IonProgressBar, selector: "ion-progress-bar", inputs: ["buffer", "color", "mode", "reversed", "type", "value"] }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: DragAndDropDirective, selector: "[appDragAndDrop]", outputs: ["fileDropped"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }] });
|
|
17664
17690
|
}
|
|
17665
17691
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: UploadFileComponent, decorators: [{
|
|
17666
17692
|
type: Component,
|
|
17667
|
-
args: [{ selector: 'app-upload-file', standalone: false, template: "@if (!showDropArea) {\n <!-- hidden file input (no drop area): used by browse() -->\n <input\n type=\"file\"\n #fileDropRef\n class=\"hidden-file-input\"\n hidden\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n} @else if ((!uniqueFile && !autoHideDropArea) || files.length === 0) {\n <div class=\"container\" appDragAndDrop (fileDropped)=\"onFileDropped($event)\">\n <input\n type=\"file\"\n #fileDropRef\n id=\"fileDropRef\"\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n <mat-icon style=\"font-size: xx-large\" color=\"accent\" class=\"text-size\">upload</mat-icon>\n <ion-label>\n <span\n [innerHTML]=\"\n 'FILE.UPLOAD.DRAG_AND_DROP' | translate: { extension: !fileExtension ? '' : ' (' + fileExtension + ')' }\n \"\n ></span>\n </ion-label>\n <ion-button color=\"tertiary\">{{ 'FILE.UPLOAD.BROWSE' | translate }}</ion-button>\n </div>\n}\n<!-- global progression (useful when many files are selected) -->\n@if (uploading && files.length > 1) {\n <div class=\"upload-summary\">\n <ion-label color=\"medium\">\n <small>{{ 'FILE.UPLOAD.DONE_COUNT' | translate: { count: doneFilesCount, total: files.length } }}</small>\n </ion-label>\n <ion-progress-bar [value]=\"doneFilesCount / files.length\" color=\"accent\"></ion-progress-bar>\n </div>\n}\n<div class=\"files-list\">\n <ion-list>\n @for (file of files; track file; let i = $index) {\n <ion-item>\n @if (showThumbnails && file.kind === 'image') {\n <!-- image thumbnail, or a skeleton until the image is ready -->\n <div slot=\"start\" class=\"file-thumbnail\">\n @if (file.error) {\n <mat-icon color=\"danger\">broken_image</mat-icon>\n } @else if (file.progress === 1 && file.response?.body) {\n <img [src]=\"file.response.body\" alt=\"\" />\n } @else {\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n }\n </div>\n } @else {\n <!-- icon of the file type (pdf, spreadsheet, document...) -->\n <div slot=\"start\" class=\"file-icon\" [class.file-thumbnail]=\"showThumbnails\" [attr.data-kind]=\"file.kind\">\n <mat-icon>{{ fileKindIcons[file.kind || 'other'] }}</mat-icon>\n </div>\n }\n <div class=\"single-file\">\n <ion-label>\n <h4 class=\"name\" [class.deleting]=\"file.deleting\">\n {{ file.name }}\n </h4>\n <p>\n {{ file.size | fileSize }}\n </p>\n </ion-label>\n @if (uploading && !file.error && !showThumbnails) {\n <ion-progress-bar\n [value]=\"file.progress || 0\"\n [type]=\"file.deleting || file.progress === -1 ? 'indeterminate' : 'determinate'\"\n ></ion-progress-bar>\n }\n @if (file.error) {\n <ion-text\n color=\"danger\"\n [innerHTML]=\"\n file.error\n | translate\n : {\n fileExtension: fileExtension,\n fileExtensionsExcluded: fileExtensionsExcluded.join(', '),\n maxFileSizeMb: maxFileSizeMb,\n }\n \"\n ></ion-text>\n }\n </div>\n <!-- remote from list (before importation) -->\n @if (!file.progress || file.error) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n <!-- delete file (remotely - after importation) -->\n @if (file.progress === 1 && !!deleteFn && !file.deleting) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n </ion-item>\n }\n </ion-list>\n</div>\n", styles: [".container{width:calc(100% - 3rem);min-height:200px;padding:2rem;text-align:center;border:dashed 1px #979797;position:relative;margin:1.5rem}.container input{opacity:0;position:absolute;z-index:2;width:100%;height:100%;top:0;left:0;cursor:pointer}.container ion-label{display:block;font-size:20px;font-weight:600;color:#38424c}.container label{display:inline-block;color:#fff;width:183px;height:44px;border-radius:21.5px;background-color:#db202f;padding:8px 16px}.fileover{border:solid 1px var(--ion-color-accent)}.upload-summary{padding:0 1.5rem .5rem}.upload-summary ion-progress-bar{margin-top:4px}.file-thumbnail{--thumbnail-size: 48px;width:var(--thumbnail-size);height:var(--thumbnail-size);flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:4px;overflow:hidden}.file-thumbnail img{width:100%;height:100%;object-fit:cover}.file-thumbnail ion-skeleton-text{width:100%;height:100%;margin:0}.file-icon{display:flex;align-items:center;justify-content:center;color:var(--ion-color-medium)}.file-icon.file-thumbnail mat-icon{--icon-size: 36px;font-size:var(--icon-size);width:var(--icon-size);height:var(--icon-size)}.file-icon[data-kind=pdf]{color:#d93025}.file-icon[data-kind=document]{color:#2b579a}.file-icon[data-kind=spreadsheet]{color:#217346}.file-icon[data-kind=presentation]{color:#d24726}.file-icon[data-kind=archive]{color:#b8860b}.file-icon[data-kind=image]{color:var(--ion-color-tertiary)}.file-icon[data-kind=video],.file-icon[data-kind=audio]{color:#7b1fa2}.files-list{max-height:300px;overflow:auto}.files-list .single-file{display:flex;flex-direction:column}.files-list .single-file .name{word-break:break-all}.files-list .single-file .name.deleting{color:gray!important;font-style:italic!important}\n"] }]
|
|
17693
|
+
args: [{ selector: 'app-upload-file', standalone: false, template: "@if (!showDropArea) {\n <!-- hidden file input (no drop area): used by browse() -->\n <input\n type=\"file\"\n #fileDropRef\n class=\"hidden-file-input\"\n hidden\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n} @else if ((!uniqueFile && !autoHideDropArea) || files.length === 0) {\n <div class=\"container\" appDragAndDrop (fileDropped)=\"onFileDropped($event)\">\n <input\n type=\"file\"\n #fileDropRef\n id=\"fileDropRef\"\n [multiple]=\"!uniqueFile\"\n [accept]=\"fileExtension\"\n (change)=\"fileBrowseHandler($any($event.target).files)\"\n />\n <mat-icon style=\"font-size: xx-large\" color=\"accent\" class=\"text-size\">upload</mat-icon>\n <ion-label>\n <span\n [innerHTML]=\"\n 'FILE.UPLOAD.DRAG_AND_DROP' | translate: { extension: !fileExtension ? '' : ' (' + fileExtension + ')' }\n \"\n ></span>\n </ion-label>\n <ion-button color=\"tertiary\">{{ 'FILE.UPLOAD.BROWSE' | translate }}</ion-button>\n </div>\n}\n<!-- global progression (useful when many files are selected) -->\n@if (uploading && files.length > 1) {\n <div class=\"upload-summary\">\n <ion-label color=\"medium\">\n <small>{{ 'FILE.UPLOAD.DONE_COUNT' | translate: { count: doneFilesCount, total: files.length } }}</small>\n </ion-label>\n <ion-progress-bar [value]=\"doneFilesCount / files.length\" color=\"accent\"></ion-progress-bar>\n </div>\n}\n<div class=\"files-list\">\n <ion-list>\n @for (file of files; track file; let i = $index) {\n <ion-item>\n @if (showThumbnails && file.kind === 'image') {\n <!-- image thumbnail, or a skeleton until the image is ready -->\n <div slot=\"start\" class=\"file-thumbnail\">\n @if (file.error) {\n <mat-icon color=\"danger\">broken_image</mat-icon>\n } @else if (file.progress === 1 && file.response?.body) {\n <img [src]=\"file.response.body\" alt=\"\" />\n } @else {\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n }\n </div>\n } @else {\n <!-- icon of the file type (pdf, spreadsheet, document...) -->\n <div slot=\"start\" class=\"file-icon\" [class.file-thumbnail]=\"showThumbnails\" [attr.data-kind]=\"file.kind\">\n <mat-icon>{{ fileKindIcons[file.kind || 'other'] }}</mat-icon>\n </div>\n }\n <div class=\"single-file\">\n <ion-label>\n <h4 class=\"name\" [class.deleting]=\"file.deleting\">\n {{ file.name }}\n </h4>\n <p>\n {{ file.size | fileSize }}\n </p>\n </ion-label>\n @if (uploading && !file.error && !showThumbnails) {\n <ion-progress-bar\n [value]=\"file.progress || 0\"\n [type]=\"file.deleting || file.progress === -1 ? 'indeterminate' : 'determinate'\"\n ></ion-progress-bar>\n }\n @if (file.error) {\n <ion-text\n color=\"danger\"\n [innerHTML]=\"\n file.error\n | translate\n : {\n fileExtension: fileExtension,\n fileExtensionsExcluded: fileExtensionsExcluded.join(', '),\n maxFileSizeMb: maxFileSizeMb,\n }\n \"\n ></ion-text>\n }\n </div>\n <!-- remote from list (before importation) -->\n @if (!file.progress || file.error) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n <!-- delete file (remotely - after importation) -->\n @if (file.progress === 1 && !!deleteFn && !file.deleting) {\n <button mat-icon-button slot=\"end\" (click)=\"deleteFile(i)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n </ion-item>\n }\n </ion-list>\n</div>\n", styles: [".container{width:calc(100% - 3rem);min-height:200px;padding:2rem;text-align:center;border:dashed 1px #979797;position:relative;margin:1.5rem}.container input{opacity:0;position:absolute;z-index:2;width:100%;height:100%;top:0;left:0;cursor:pointer}.container ion-label{display:block;font-size:20px;font-weight:600;color:#38424c}.container label{display:inline-block;color:#fff;width:183px;height:44px;border-radius:21.5px;background-color:#db202f;padding:8px 16px}.fileover{border:solid 1px var(--ion-color-accent)}.upload-summary{padding:0 1.5rem .5rem}.upload-summary ion-progress-bar{margin-top:4px}.file-thumbnail{--thumbnail-size: 48px;width:var(--thumbnail-size);height:var(--thumbnail-size);flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:4px;overflow:hidden}.file-thumbnail img{width:100%;height:100%;object-fit:cover}.file-thumbnail ion-skeleton-text{width:100%;height:100%;margin:0}.file-icon{display:flex;align-items:center;justify-content:center;color:var(--ion-color-medium)}.file-icon.file-thumbnail mat-icon{--icon-size: 36px;font-size:var(--icon-size);width:var(--icon-size);height:var(--icon-size)}.file-icon[data-kind=pdf]{color:#d93025}.file-icon[data-kind=document]{color:#2b579a}.file-icon[data-kind=spreadsheet]{color:#217346}.file-icon[data-kind=presentation]{color:#d24726}.file-icon[data-kind=archive]{color:#b8860b}.file-icon[data-kind=image]{color:var(--ion-color-tertiary)}.file-icon[data-kind=video],.file-icon[data-kind=audio]{color:#7b1fa2}.files-list{max-height:var(--app-upload-file-list-max-height, 300px);overflow:auto}.files-list .single-file{display:flex;flex-direction:column;flex:1 1 auto}.files-list .single-file .name{word-break:break-all}.files-list .single-file .name.deleting{color:gray!important;font-style:italic!important}\n"] }]
|
|
17668
17694
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i2.TranslateService }], propDecorators: { fileDropEl: [{
|
|
17669
17695
|
type: ViewChild,
|
|
17670
17696
|
args: ['fileDropRef', { static: false }]
|
|
@@ -17712,6 +17738,7 @@ class UploadFilePopover {
|
|
|
17712
17738
|
showThumbnails = false;
|
|
17713
17739
|
okButtonText = 'COMMON.BTN_IMPORT';
|
|
17714
17740
|
cancelButtonText = 'COMMON.BTN_CANCEL';
|
|
17741
|
+
showOkButton = true;
|
|
17715
17742
|
importing = false;
|
|
17716
17743
|
error;
|
|
17717
17744
|
get files() {
|
|
@@ -17768,6 +17795,17 @@ class UploadFilePopover {
|
|
|
17768
17795
|
cancel() {
|
|
17769
17796
|
return this.popoverController.dismiss();
|
|
17770
17797
|
}
|
|
17798
|
+
async onFilesChange(files) {
|
|
17799
|
+
if (this.showOkButton || this.importing)
|
|
17800
|
+
return;
|
|
17801
|
+
if (isEmptyArray(files))
|
|
17802
|
+
return;
|
|
17803
|
+
const processedFiles = this.uploader.processedFiles;
|
|
17804
|
+
const allFilesProcessed = files.every((file) => !!file.error || (processedFiles.includes(file) && isNotNil(file.response)));
|
|
17805
|
+
if (!allFilesProcessed)
|
|
17806
|
+
return;
|
|
17807
|
+
await this.onValidate(null);
|
|
17808
|
+
}
|
|
17771
17809
|
resetError() {
|
|
17772
17810
|
if (this.error) {
|
|
17773
17811
|
this.error = null;
|
|
@@ -17775,11 +17813,11 @@ class UploadFilePopover {
|
|
|
17775
17813
|
}
|
|
17776
17814
|
}
|
|
17777
17815
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: UploadFilePopover, deps: [{ token: i2$2.PopoverController }, { token: i2.TranslateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
17778
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: UploadFilePopover, isStandalone: false, selector: "app-upload-file-popover", inputs: { fileExtension: "fileExtension", fileExtensionsExcluded: "fileExtensionsExcluded", title: "title", uniqueFile: "uniqueFile", instantUpload: "instantUpload", uploadFn: "uploadFn", deleteFn: "deleteFn", maxParallelUpload: "maxParallelUpload", maxFileSizeMb: "maxFileSizeMb", autoHideDropArea: "autoHideDropArea", showThumbnails: "showThumbnails", okButtonText: "okButtonText", cancelButtonText: "cancelButtonText" }, viewQueries: [{ propertyName: "uploader", first: true, predicate: ["uploader"], descendants: true, static: true }], ngImport: i0, template: "<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [fileExtensionsExcluded]=\"fileExtensionsExcluded\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [autoHideDropArea]=\"autoHideDropArea\"\n [showThumbnails]=\"showThumbnails\"\n ></app-upload-file>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding ion-nowrap\">\n <ion-col>\n @if (importing) {\n <div class=\"importing-info\">\n <ion-text color=\"medium\"><small>{{ 'FILE.UPLOAD.IMPORTING' | translate }}</small></ion-text>\n \n <ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n }\n @if (error) {\n <ion-item lines=\"none\" color=\"transparent\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\">\n <small [innerHTML]=\"error | translate\"></small>\n </ion-label>\n </ion-item>\n }\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text [innerHTML]=\"cancelButtonText | translate\"></ion-text>\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-text [innerHTML]=\"okButtonText | translate\"></ion-text>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", styles: [".importing-info{padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: UploadFileComponent, selector: "app-upload-file", inputs: ["fileExtension", "fileExtensionsExcluded", "uniqueFile", "instantUpload", "uploadFn", "deleteFn", "maxParallelUpload", "maxFileSizeMb", "autoHideDropArea", "showDropArea", "showThumbnails"], outputs: ["filesChange"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
17816
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: UploadFilePopover, isStandalone: false, selector: "app-upload-file-popover", inputs: { fileExtension: "fileExtension", fileExtensionsExcluded: "fileExtensionsExcluded", title: "title", uniqueFile: "uniqueFile", instantUpload: "instantUpload", uploadFn: "uploadFn", deleteFn: "deleteFn", maxParallelUpload: "maxParallelUpload", maxFileSizeMb: "maxFileSizeMb", autoHideDropArea: "autoHideDropArea", showThumbnails: "showThumbnails", okButtonText: "okButtonText", cancelButtonText: "cancelButtonText", showOkButton: "showOkButton" }, viewQueries: [{ propertyName: "uploader", first: true, predicate: ["uploader"], descendants: true, static: true }], ngImport: i0, template: "<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [fileExtensionsExcluded]=\"fileExtensionsExcluded\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [autoHideDropArea]=\"autoHideDropArea\"\n [showThumbnails]=\"showThumbnails\"\n (filesChange)=\"onFilesChange($event)\"\n ></app-upload-file>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding ion-nowrap\">\n <ion-col>\n @if (importing) {\n <div class=\"importing-info\">\n <ion-text color=\"medium\"><small>{{ 'FILE.UPLOAD.IMPORTING' | translate }}</small></ion-text>\n \n <ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n }\n @if (error) {\n <ion-item lines=\"none\" color=\"transparent\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\">\n <small [innerHTML]=\"error | translate\"></small>\n </ion-label>\n </ion-item>\n }\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text [innerHTML]=\"cancelButtonText | translate\"></ion-text>\n </ion-button>\n @if (showOkButton) {\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-text [innerHTML]=\"okButtonText | translate\"></ion-text>\n </ion-button>\n }\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", styles: [".importing-info{padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: UploadFileComponent, selector: "app-upload-file", inputs: ["fileExtension", "fileExtensionsExcluded", "uniqueFile", "instantUpload", "uploadFn", "deleteFn", "maxParallelUpload", "maxFileSizeMb", "autoHideDropArea", "showDropArea", "showThumbnails"], outputs: ["filesChange"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
17779
17817
|
}
|
|
17780
17818
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: UploadFilePopover, decorators: [{
|
|
17781
17819
|
type: Component,
|
|
17782
|
-
args: [{ selector: 'app-upload-file-popover', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [fileExtensionsExcluded]=\"fileExtensionsExcluded\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [autoHideDropArea]=\"autoHideDropArea\"\n [showThumbnails]=\"showThumbnails\"\n ></app-upload-file>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding ion-nowrap\">\n <ion-col>\n @if (importing) {\n <div class=\"importing-info\">\n <ion-text color=\"medium\"><small>{{ 'FILE.UPLOAD.IMPORTING' | translate }}</small></ion-text>\n \n <ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n }\n @if (error) {\n <ion-item lines=\"none\" color=\"transparent\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\">\n <small [innerHTML]=\"error | translate\"></small>\n </ion-label>\n </ion-item>\n }\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text [innerHTML]=\"cancelButtonText | translate\"></ion-text>\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-text [innerHTML]=\"okButtonText | translate\"></ion-text>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", styles: [".importing-info{padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}\n"] }]
|
|
17820
|
+
args: [{ selector: 'app-upload-file-popover', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [fileExtensionsExcluded]=\"fileExtensionsExcluded\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [autoHideDropArea]=\"autoHideDropArea\"\n [showThumbnails]=\"showThumbnails\"\n (filesChange)=\"onFilesChange($event)\"\n ></app-upload-file>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding ion-nowrap\">\n <ion-col>\n @if (importing) {\n <div class=\"importing-info\">\n <ion-text color=\"medium\"><small>{{ 'FILE.UPLOAD.IMPORTING' | translate }}</small></ion-text>\n \n <ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n }\n @if (error) {\n <ion-item lines=\"none\" color=\"transparent\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\">\n <small [innerHTML]=\"error | translate\"></small>\n </ion-label>\n </ion-item>\n }\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text [innerHTML]=\"cancelButtonText | translate\"></ion-text>\n </ion-button>\n @if (showOkButton) {\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-text [innerHTML]=\"okButtonText | translate\"></ion-text>\n </ion-button>\n }\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", styles: [".importing-info{padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}\n"] }]
|
|
17783
17821
|
}], ctorParameters: () => [{ type: i2$2.PopoverController }, { type: i2.TranslateService }, { type: i0.ChangeDetectorRef }], propDecorators: { uploader: [{
|
|
17784
17822
|
type: ViewChild,
|
|
17785
17823
|
args: ['uploader', { static: true }]
|
|
@@ -17809,38 +17847,223 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
17809
17847
|
type: Input
|
|
17810
17848
|
}], cancelButtonText: [{
|
|
17811
17849
|
type: Input
|
|
17850
|
+
}], showOkButton: [{
|
|
17851
|
+
type: Input
|
|
17812
17852
|
}] } });
|
|
17813
17853
|
|
|
17814
17854
|
const APP_TESTING_PAGES = new InjectionToken('testingPages');
|
|
17855
|
+
const SEARCH_QUERY_PARAM = 'q';
|
|
17856
|
+
function normalize(value) {
|
|
17857
|
+
return removeDiacritics(value || '').toLowerCase();
|
|
17858
|
+
}
|
|
17859
|
+
/**
|
|
17860
|
+
* Split a text into parts, flagging parts that match one of the (normalized) tokens.
|
|
17861
|
+
* Assumes removeDiacritics() keeps the string length (true for latin accents).
|
|
17862
|
+
*/
|
|
17863
|
+
function splitByTokens(text, tokens) {
|
|
17864
|
+
if (!text)
|
|
17865
|
+
return [];
|
|
17866
|
+
if (!tokens.length)
|
|
17867
|
+
return [{ text, match: false }];
|
|
17868
|
+
const normalized = normalize(text);
|
|
17869
|
+
const matches = new Array(text.length).fill(false);
|
|
17870
|
+
for (const token of tokens) {
|
|
17871
|
+
let start = normalized.indexOf(token);
|
|
17872
|
+
while (start !== -1) {
|
|
17873
|
+
matches.fill(true, start, start + token.length);
|
|
17874
|
+
start = normalized.indexOf(token, start + token.length);
|
|
17875
|
+
}
|
|
17876
|
+
}
|
|
17877
|
+
const parts = [];
|
|
17878
|
+
for (let i = 0; i < text.length; i++) {
|
|
17879
|
+
const last = parts[parts.length - 1];
|
|
17880
|
+
if (last && last.match === matches[i])
|
|
17881
|
+
last.text += text[i];
|
|
17882
|
+
else
|
|
17883
|
+
parts.push({ text: text[i], match: matches[i] });
|
|
17884
|
+
}
|
|
17885
|
+
return parts;
|
|
17886
|
+
}
|
|
17815
17887
|
class SharedTestsPage {
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
17825
|
-
|
|
17888
|
+
// Code under free license (GPL version 3)
|
|
17889
|
+
searchbar = viewChild(IonSearchbar, ...(ngDevMode ? [{ debugName: "searchbar" }] : /* istanbul ignore next */ []));
|
|
17890
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : /* istanbul ignore next */ []));
|
|
17891
|
+
activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
17892
|
+
showSearchbar = signal(false, ...(ngDevMode ? [{ debugName: "showSearchbar" }] : /* istanbul ignore next */ []));
|
|
17893
|
+
totalCount;
|
|
17894
|
+
initialSearchText; // Not bound to searchText(), to avoid overriding the input while typing
|
|
17895
|
+
sections = computed(() => this.filter(this.searchText()), ...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
|
|
17896
|
+
visibleCount = computed(() => this.sections().reduce((count, section) => count + section.items.length, 0), ...(ngDevMode ? [{ debugName: "visibleCount" }] : /* istanbul ignore next */ []));
|
|
17897
|
+
route = inject(ActivatedRoute);
|
|
17898
|
+
router = inject(Router);
|
|
17899
|
+
location = inject(Location);
|
|
17900
|
+
pages = inject(APP_TESTING_PAGES, { optional: true });
|
|
17901
|
+
constructor() {
|
|
17902
|
+
console.info('[testing] Loaded tests pages:', this.pages);
|
|
17903
|
+
this.totalCount = (this.pages || []).filter((p) => !p.divider && p.page).length;
|
|
17904
|
+
// Restore the search text (e.g. when going back from a test page)
|
|
17905
|
+
this.initialSearchText = this.route.snapshot.queryParamMap.get(SEARCH_QUERY_PARAM) || '';
|
|
17906
|
+
this.searchText.set(this.initialSearchText);
|
|
17907
|
+
this.showSearchbar.set(!!this.initialSearchText);
|
|
17908
|
+
}
|
|
17909
|
+
ionViewDidEnter() {
|
|
17910
|
+
// Focus the searchbar (if visible), to be able to continue typing
|
|
17911
|
+
if (this.showSearchbar())
|
|
17912
|
+
this.focusSearchbar();
|
|
17913
|
+
}
|
|
17914
|
+
onDocumentKeydown(domEvent) {
|
|
17915
|
+
const event = domEvent;
|
|
17916
|
+
// Open the searchbar on '/' or Ctrl+K, if not already typing in a field
|
|
17917
|
+
const target = event.target;
|
|
17918
|
+
const isTyping = target?.closest?.('input, textarea, [contenteditable="true"]');
|
|
17919
|
+
if ((event.key === '/' && !isTyping) || (event.key === 'k' && (event.ctrlKey || event.metaKey))) {
|
|
17920
|
+
event.preventDefault();
|
|
17921
|
+
this.openSearchbar();
|
|
17922
|
+
}
|
|
17923
|
+
}
|
|
17924
|
+
toggleSearchbar() {
|
|
17925
|
+
if (this.showSearchbar())
|
|
17926
|
+
this.closeSearchbar();
|
|
17927
|
+
else
|
|
17928
|
+
this.openSearchbar();
|
|
17929
|
+
}
|
|
17930
|
+
openSearchbar() {
|
|
17931
|
+
this.showSearchbar.set(true);
|
|
17932
|
+
this.focusSearchbar();
|
|
17933
|
+
}
|
|
17934
|
+
closeSearchbar() {
|
|
17935
|
+
// Reset the filter, to never hide pages without a visible searchbar
|
|
17936
|
+
if (this.searchText())
|
|
17937
|
+
this.onSearchInput('');
|
|
17938
|
+
this.initialSearchText = '';
|
|
17939
|
+
this.showSearchbar.set(false);
|
|
17940
|
+
}
|
|
17941
|
+
onSearchInput(value) {
|
|
17942
|
+
const searchText = value || '';
|
|
17943
|
+
this.searchText.set(searchText);
|
|
17944
|
+
this.activeIndex.set(0);
|
|
17945
|
+
// Keep the search text in the URL (restored on back navigation), without triggering a navigation
|
|
17946
|
+
const urlTree = this.router.createUrlTree([], {
|
|
17947
|
+
relativeTo: this.route,
|
|
17948
|
+
queryParams: { [SEARCH_QUERY_PARAM]: searchText.trim() || null },
|
|
17949
|
+
queryParamsHandling: 'merge',
|
|
17950
|
+
});
|
|
17951
|
+
this.location.replaceState(this.router.serializeUrl(urlTree));
|
|
17952
|
+
}
|
|
17953
|
+
onSearchKeydown(event) {
|
|
17954
|
+
const count = this.visibleCount();
|
|
17955
|
+
switch (event.key) {
|
|
17956
|
+
case 'ArrowDown':
|
|
17957
|
+
event.preventDefault();
|
|
17958
|
+
if (count)
|
|
17959
|
+
this.setActiveIndex((this.activeIndex() + 1) % count);
|
|
17960
|
+
break;
|
|
17961
|
+
case 'ArrowUp':
|
|
17962
|
+
event.preventDefault();
|
|
17963
|
+
if (count)
|
|
17964
|
+
this.setActiveIndex((this.activeIndex() - 1 + count) % count);
|
|
17965
|
+
break;
|
|
17966
|
+
case 'Enter': {
|
|
17967
|
+
event.preventDefault();
|
|
17968
|
+
const item = this.findItem(this.activeIndex());
|
|
17969
|
+
if (item)
|
|
17970
|
+
this.openPage(item.page, event);
|
|
17971
|
+
break;
|
|
17972
|
+
}
|
|
17973
|
+
case 'Escape':
|
|
17974
|
+
// Clear the search text, or close the searchbar if already empty
|
|
17975
|
+
event.preventDefault();
|
|
17976
|
+
if (this.searchText())
|
|
17977
|
+
this.clearSearch();
|
|
17978
|
+
else
|
|
17979
|
+
this.closeSearchbar();
|
|
17980
|
+
break;
|
|
17981
|
+
}
|
|
17982
|
+
}
|
|
17983
|
+
clearSearch() {
|
|
17984
|
+
const searchbar = this.searchbar();
|
|
17985
|
+
if (searchbar)
|
|
17986
|
+
searchbar.value = '';
|
|
17987
|
+
this.onSearchInput('');
|
|
17988
|
+
this.focusSearchbar();
|
|
17826
17989
|
}
|
|
17827
|
-
|
|
17990
|
+
focusSearchbar() {
|
|
17991
|
+
// Wait the searchbar to be rendered
|
|
17992
|
+
setTimeout(() => this.searchbar()?.setFocus(), 150);
|
|
17993
|
+
}
|
|
17994
|
+
async openPage(page, event) {
|
|
17995
|
+
// Open in a new tab, on Ctrl/Cmd + click (or Ctrl/Cmd + Enter)
|
|
17996
|
+
if (event && (event.ctrlKey || event.metaKey)) {
|
|
17997
|
+
const url = this.router.serializeUrl(this.router.createUrlTree([page]));
|
|
17998
|
+
window.open(url, '_blank');
|
|
17999
|
+
return false;
|
|
18000
|
+
}
|
|
17828
18001
|
return this.router.navigate([page], {
|
|
17829
18002
|
relativeTo: this.route,
|
|
17830
18003
|
});
|
|
17831
18004
|
}
|
|
17832
|
-
|
|
17833
|
-
|
|
18005
|
+
setActiveIndex(index) {
|
|
18006
|
+
this.activeIndex.set(index);
|
|
18007
|
+
// Scroll the active item into view
|
|
18008
|
+
setTimeout(() => document.querySelector('app-tests-page ion-item.active')?.scrollIntoView({ block: 'nearest' }));
|
|
18009
|
+
}
|
|
18010
|
+
findItem(index) {
|
|
18011
|
+
for (const section of this.sections()) {
|
|
18012
|
+
const item = section.items.find((i) => i.index === index);
|
|
18013
|
+
if (item)
|
|
18014
|
+
return item;
|
|
18015
|
+
}
|
|
18016
|
+
return undefined;
|
|
18017
|
+
}
|
|
18018
|
+
filter(searchText) {
|
|
18019
|
+
const tokens = normalize(searchText)
|
|
18020
|
+
.split(/\s+/)
|
|
18021
|
+
.filter((token) => token.length > 0);
|
|
18022
|
+
// Group pages by section (divider)
|
|
18023
|
+
const groups = [];
|
|
18024
|
+
for (const page of this.pages || []) {
|
|
18025
|
+
if (page.divider)
|
|
18026
|
+
groups.push({ label: page.label, pages: [] });
|
|
18027
|
+
else if (page.page) {
|
|
18028
|
+
if (!groups.length)
|
|
18029
|
+
groups.push({ pages: [] });
|
|
18030
|
+
groups[groups.length - 1].pages.push(page);
|
|
18031
|
+
}
|
|
18032
|
+
}
|
|
18033
|
+
// Filter: each token must match the item label, its path, or its section label
|
|
18034
|
+
let index = 0;
|
|
18035
|
+
const sections = [];
|
|
18036
|
+
for (const group of groups) {
|
|
18037
|
+
const sectionText = normalize(group.label);
|
|
18038
|
+
const items = group.pages
|
|
18039
|
+
.filter((p) => {
|
|
18040
|
+
if (!tokens.length)
|
|
18041
|
+
return true;
|
|
18042
|
+
const text = `${sectionText} ${normalize(p.label)} ${normalize(p.page)}`;
|
|
18043
|
+
return tokens.every((token) => text.includes(token));
|
|
18044
|
+
})
|
|
18045
|
+
.map((p) => ({
|
|
18046
|
+
index: index++,
|
|
18047
|
+
page: p.page,
|
|
18048
|
+
labelParts: splitByTokens(p.label, tokens),
|
|
18049
|
+
pageParts: splitByTokens(p.page, tokens),
|
|
18050
|
+
}));
|
|
18051
|
+
if (items.length) {
|
|
18052
|
+
sections.push({ labelParts: group.label ? splitByTokens(group.label, tokens) : undefined, items });
|
|
18053
|
+
}
|
|
18054
|
+
}
|
|
18055
|
+
return sections;
|
|
18056
|
+
}
|
|
18057
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedTestsPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18058
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: SharedTestsPage, isStandalone: false, selector: "app-tests-page", host: { listeners: { "document:keydown": "onDocumentKeydown($event)" } }, viewQueries: [{ propertyName: "searchbar", first: true, predicate: IonSearchbar, descendants: true, isSignal: true }], ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-menu-button></ion-menu-button>\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n @if (showSearchbar()) {\n <!-- Search bar (replace the title, when expanded) -->\n <ion-searchbar\n color=\"light\"\n [value]=\"initialSearchText\"\n [debounce]=\"0\"\n [placeholder]=\"'TESTING_PAGE.SEARCH_PLACEHOLDER' | translate\"\n showClearButton=\"always\"\n autocapitalize=\"off\"\n (ionInput)=\"onSearchInput($event.detail.value)\"\n (ionClear)=\"clearSearch()\"\n (keydown)=\"onSearchKeydown($event)\"\n ></ion-searchbar>\n } @else {\n <ion-title>{{ 'TESTING_PAGE.TITLE' | translate }}</ion-title>\n }\n\n <ion-buttons slot=\"end\">\n <!-- Toggle the search bar -->\n <ion-button\n [class.search-active]=\"showSearchbar()\"\n [title]=\"(showSearchbar() ? 'TESTING_PAGE.BTN_HIDE_SEARCH' : 'TESTING_PAGE.BTN_SHOW_SEARCH') | translate\"\n (click)=\"toggleSearchbar()\"\n >\n <ion-icon slot=\"icon-only\" name=\"search\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n\n <!-- Result count (under the search bar) -->\n @if (showSearchbar() && searchText().trim()) {\n <div class=\"search-count\">\n <ion-label [color]=\"visibleCount() === 0 ? 'danger' : 'primary100'\" class=\"ion-padding\">\n {{ (visibleCount() ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT') | translate: { count: visibleCount() } }}\n </ion-label>\n </div>\n }\n</ion-header>\n\n<ion-content>\n @if (sections().length) {\n <ion-list>\n @for (section of sections(); track $index) {\n <ion-item-group>\n @if (section.labelParts) {\n <ion-item-divider sticky>\n <ion-label>\n <ng-container *ngTemplateOutlet=\"textParts; context: { $implicit: section.labelParts }\"></ng-container>\n </ion-label>\n </ion-item-divider>\n }\n @for (item of section.items; track item.page) {\n <ion-item\n tappable\n class=\"text-1x\"\n [class.active]=\"searchText() && item.index === activeIndex()\"\n [title]=\"item.page\"\n (click)=\"openPage(item.page, $event)\"\n (auxclick)=\"$event.button === 1 && openPage(item.page, { ctrlKey: true })\"\n >\n <ion-label>\n <ng-container *ngTemplateOutlet=\"textParts; context: { $implicit: item.labelParts }\"></ng-container>\n </ion-label>\n <ion-note slot=\"end\" class=\"page-path ion-hide-sm-down\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { $implicit: item.pageParts }\"></ng-container>\n </ion-note>\n </ion-item>\n }\n </ion-item-group>\n }\n </ion-list>\n } @else if (searchText()) {\n <!-- No result: the message is displayed under the search bar -->\n <div class=\"ion-padding ion-text-center\">\n <ion-button fill=\"clear\" (click)=\"clearSearch()\">\n <ion-icon slot=\"start\" name=\"close\"></ion-icon>\n {{ 'TESTING_PAGE.BTN_CLEAR_SEARCH' | translate }}\n </ion-button>\n </div>\n }\n</ion-content>\n\n<ng-template #textParts let-parts>\n <!-- Keep on a single line, to avoid extra spaces around <mark> -->\n <!-- prettier-ignore -->\n @for (part of parts; track $index) {@if (part.match) {<mark>{{ part.text }}</mark>} @else {{{ part.text }}}}\n</ng-template>\n", styles: [":host ion-toolbar ion-searchbar{padding-top:4px;padding-bottom:4px}:host .search-count{display:flex;background:var(--ion-background-color, #fff);border-bottom:1px solid var(--ion-border-color, var(--ion-color-light-shade))}:host ion-button.search-active{--background: rgba(var(--ion-color-primary-contrast-rgb, 255, 255, 255), .2);--border-radius: 50%}:host ion-item.active{--background: var(--ion-color-light);box-shadow:inset 3px 0 0 var(--ion-color-primary)}:host .page-path{font-family:monospace;font-size:.8em}:host mark{background-color:var(--ion-color-warning-tint, #ffe187);color:inherit;padding:0;border-radius:2px}\n"], dependencies: [{ kind: "directive", type: i2$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2$2.IonBackButton, selector: "ion-back-button" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { kind: "component", type: i2$2.IonItemGroup, selector: "ion-item-group" }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$2.IonMenuButton, selector: "ion-menu-button", inputs: ["autoHide", "color", "disabled", "menu", "mode", "type"] }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonSearchbar, selector: "ion-searchbar", inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "name", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
17834
18059
|
}
|
|
17835
18060
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedTestsPage, decorators: [{
|
|
17836
18061
|
type: Component,
|
|
17837
|
-
args: [{ selector: 'app-tests-page', standalone: false, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-menu-button></ion-menu-button>\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>
|
|
17838
|
-
}], ctorParameters: () => [
|
|
17839
|
-
|
|
17840
|
-
|
|
17841
|
-
|
|
17842
|
-
args: [APP_TESTING_PAGES]
|
|
17843
|
-
}] }] });
|
|
18062
|
+
args: [{ selector: 'app-tests-page', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-menu-button></ion-menu-button>\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n @if (showSearchbar()) {\n <!-- Search bar (replace the title, when expanded) -->\n <ion-searchbar\n color=\"light\"\n [value]=\"initialSearchText\"\n [debounce]=\"0\"\n [placeholder]=\"'TESTING_PAGE.SEARCH_PLACEHOLDER' | translate\"\n showClearButton=\"always\"\n autocapitalize=\"off\"\n (ionInput)=\"onSearchInput($event.detail.value)\"\n (ionClear)=\"clearSearch()\"\n (keydown)=\"onSearchKeydown($event)\"\n ></ion-searchbar>\n } @else {\n <ion-title>{{ 'TESTING_PAGE.TITLE' | translate }}</ion-title>\n }\n\n <ion-buttons slot=\"end\">\n <!-- Toggle the search bar -->\n <ion-button\n [class.search-active]=\"showSearchbar()\"\n [title]=\"(showSearchbar() ? 'TESTING_PAGE.BTN_HIDE_SEARCH' : 'TESTING_PAGE.BTN_SHOW_SEARCH') | translate\"\n (click)=\"toggleSearchbar()\"\n >\n <ion-icon slot=\"icon-only\" name=\"search\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n\n <!-- Result count (under the search bar) -->\n @if (showSearchbar() && searchText().trim()) {\n <div class=\"search-count\">\n <ion-label [color]=\"visibleCount() === 0 ? 'danger' : 'primary100'\" class=\"ion-padding\">\n {{ (visibleCount() ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT') | translate: { count: visibleCount() } }}\n </ion-label>\n </div>\n }\n</ion-header>\n\n<ion-content>\n @if (sections().length) {\n <ion-list>\n @for (section of sections(); track $index) {\n <ion-item-group>\n @if (section.labelParts) {\n <ion-item-divider sticky>\n <ion-label>\n <ng-container *ngTemplateOutlet=\"textParts; context: { $implicit: section.labelParts }\"></ng-container>\n </ion-label>\n </ion-item-divider>\n }\n @for (item of section.items; track item.page) {\n <ion-item\n tappable\n class=\"text-1x\"\n [class.active]=\"searchText() && item.index === activeIndex()\"\n [title]=\"item.page\"\n (click)=\"openPage(item.page, $event)\"\n (auxclick)=\"$event.button === 1 && openPage(item.page, { ctrlKey: true })\"\n >\n <ion-label>\n <ng-container *ngTemplateOutlet=\"textParts; context: { $implicit: item.labelParts }\"></ng-container>\n </ion-label>\n <ion-note slot=\"end\" class=\"page-path ion-hide-sm-down\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { $implicit: item.pageParts }\"></ng-container>\n </ion-note>\n </ion-item>\n }\n </ion-item-group>\n }\n </ion-list>\n } @else if (searchText()) {\n <!-- No result: the message is displayed under the search bar -->\n <div class=\"ion-padding ion-text-center\">\n <ion-button fill=\"clear\" (click)=\"clearSearch()\">\n <ion-icon slot=\"start\" name=\"close\"></ion-icon>\n {{ 'TESTING_PAGE.BTN_CLEAR_SEARCH' | translate }}\n </ion-button>\n </div>\n }\n</ion-content>\n\n<ng-template #textParts let-parts>\n <!-- Keep on a single line, to avoid extra spaces around <mark> -->\n <!-- prettier-ignore -->\n @for (part of parts; track $index) {@if (part.match) {<mark>{{ part.text }}</mark>} @else {{{ part.text }}}}\n</ng-template>\n", styles: [":host ion-toolbar ion-searchbar{padding-top:4px;padding-bottom:4px}:host .search-count{display:flex;background:var(--ion-background-color, #fff);border-bottom:1px solid var(--ion-border-color, var(--ion-color-light-shade))}:host ion-button.search-active{--background: rgba(var(--ion-color-primary-contrast-rgb, 255, 255, 255), .2);--border-radius: 50%}:host ion-item.active{--background: var(--ion-color-light);box-shadow:inset 3px 0 0 var(--ion-color-primary)}:host .page-path{font-family:monospace;font-size:.8em}:host mark{background-color:var(--ion-color-warning-tint, #ffe187);color:inherit;padding:0;border-radius:2px}\n"] }]
|
|
18063
|
+
}], ctorParameters: () => [], propDecorators: { searchbar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => IonSearchbar), { isSignal: true }] }], onDocumentKeydown: [{
|
|
18064
|
+
type: HostListener,
|
|
18065
|
+
args: ['document:keydown', ['$event']]
|
|
18066
|
+
}] } });
|
|
17844
18067
|
|
|
17845
18068
|
const StorageDrivers = {
|
|
17846
18069
|
SQLLite: CordovaSQLiteDriver._driver,
|
|
@@ -22632,6 +22855,47 @@ function isSubscriptionOperation(operation) {
|
|
|
22632
22855
|
const def = getMainDefinition(operation.query);
|
|
22633
22856
|
return def.kind === 'OperationDefinition' && def.operation === 'subscription';
|
|
22634
22857
|
}
|
|
22858
|
+
const DEFER_DIRECTIVE_NAME = 'defer';
|
|
22859
|
+
/**
|
|
22860
|
+
* Return true if the document uses the @defer directive
|
|
22861
|
+
*/
|
|
22862
|
+
function hasDeferDirective(document) {
|
|
22863
|
+
if (!document)
|
|
22864
|
+
return false;
|
|
22865
|
+
let found = false;
|
|
22866
|
+
visit(document, {
|
|
22867
|
+
[Kind.DIRECTIVE]: (node) => {
|
|
22868
|
+
if (node.name.value === DEFER_DIRECTIVE_NAME)
|
|
22869
|
+
found = true;
|
|
22870
|
+
},
|
|
22871
|
+
});
|
|
22872
|
+
return found;
|
|
22873
|
+
}
|
|
22874
|
+
/**
|
|
22875
|
+
* Remove the @defer directives from a document (deferred fragments are then fetched with the rest of the query)
|
|
22876
|
+
*/
|
|
22877
|
+
function removeDeferDirectives(document) {
|
|
22878
|
+
if (!hasDeferDirective(document))
|
|
22879
|
+
return document;
|
|
22880
|
+
return visit(document, {
|
|
22881
|
+
[Kind.DIRECTIVE]: (node) => (node.name.value === DEFER_DIRECTIVE_NAME ? null : undefined),
|
|
22882
|
+
});
|
|
22883
|
+
}
|
|
22884
|
+
function isDeferredOperation(operation) {
|
|
22885
|
+
return hasDeferDirective(operation.query);
|
|
22886
|
+
}
|
|
22887
|
+
/**
|
|
22888
|
+
* Convert Angular HttpHeaders (used by the apollo-angular HttpLink) into a plain object, as expected by the Apollo fetch HttpLink
|
|
22889
|
+
*/
|
|
22890
|
+
const httpHeadersToRecordLink = new ApolloLink((operation, forward) => {
|
|
22891
|
+
const { headers } = operation.getContext();
|
|
22892
|
+
if (headers instanceof HttpHeaders) {
|
|
22893
|
+
const record = {};
|
|
22894
|
+
headers.keys().forEach((key) => (record[key] = headers.getAll(key).join(', ')));
|
|
22895
|
+
operation.setContext({ headers: record });
|
|
22896
|
+
}
|
|
22897
|
+
return forward(operation);
|
|
22898
|
+
});
|
|
22635
22899
|
const TRACKED_QUERIES_STORAGE_KEY = 'apollo-tracker-persist';
|
|
22636
22900
|
function createTrackerLink(opts) {
|
|
22637
22901
|
const trackedQueriesUpdated = new EventEmitter();
|
|
@@ -22746,6 +23010,12 @@ class GraphqlService extends StartableService {
|
|
|
22746
23010
|
get cache() {
|
|
22747
23011
|
return this.apollo.client.cache;
|
|
22748
23012
|
}
|
|
23013
|
+
/**
|
|
23014
|
+
* Is the @defer directive enabled (see environment.enableGraphqlDefer)? If not, @defer directives are removed from queries
|
|
23015
|
+
*/
|
|
23016
|
+
get deferEnabled() {
|
|
23017
|
+
return this.environment.enableGraphqlDefer === true;
|
|
23018
|
+
}
|
|
22749
23019
|
get defaultFetchPolicy() {
|
|
22750
23020
|
return this._defaultFetchPolicy;
|
|
22751
23021
|
}
|
|
@@ -22792,7 +23062,11 @@ class GraphqlService extends StartableService {
|
|
|
22792
23062
|
});
|
|
22793
23063
|
}
|
|
22794
23064
|
queryRefValuesChanges(queryRef, opts) {
|
|
22795
|
-
return queryRef.valueChanges.pipe(catchError(async (error) => this.toApolloError(error, opts.error)),
|
|
23065
|
+
return queryRef.valueChanges.pipe(catchError(async (error) => this.toApolloError(error, opts.error)),
|
|
23066
|
+
// Emit complete results, errors, and partial results of a @defer query (streaming)
|
|
23067
|
+
filter((value) => value.networkStatus === NetworkStatus.ready ||
|
|
23068
|
+
value.networkStatus === NetworkStatus.error ||
|
|
23069
|
+
value.networkStatus === NetworkStatus.streaming), map(({ data, error }) => {
|
|
22796
23070
|
if (error) {
|
|
22797
23071
|
throw error;
|
|
22798
23072
|
}
|
|
@@ -33503,6 +33777,11 @@ class GitlabUtils {
|
|
|
33503
33777
|
}
|
|
33504
33778
|
}
|
|
33505
33779
|
|
|
33780
|
+
/** Default placeholder inserted inside wrap markers, when the selection is empty. */
|
|
33781
|
+
const WRAP_PLACEHOLDER = 'text';
|
|
33782
|
+
/** Default placeholders inserted by insertLink, when the selection is empty. */
|
|
33783
|
+
const LINK_PLACEHOLDER = 'link';
|
|
33784
|
+
const LINK_URL_PLACEHOLDER = 'url';
|
|
33506
33785
|
class MarkdownUtils {
|
|
33507
33786
|
static HTML_ENTITIES_MAP = {
|
|
33508
33787
|
''': "'", // Single quote
|
|
@@ -33577,6 +33856,128 @@ class MarkdownUtils {
|
|
|
33577
33856
|
return UrlUtils.getRootUrl(url);
|
|
33578
33857
|
}
|
|
33579
33858
|
}
|
|
33859
|
+
/** Markdown heading markers, longest first (used to replace an existing heading level). */
|
|
33860
|
+
const HEADING_MARKERS = ['###### ', '##### ', '#### ', '### ', '## ', '# '];
|
|
33861
|
+
/**
|
|
33862
|
+
* Wraps (or unwraps when already wrapped) the textarea selection with the given markdown markers.
|
|
33863
|
+
* Pure function: the caller is responsible for applying the result to the textarea and the control value.
|
|
33864
|
+
*
|
|
33865
|
+
* @param state current text and selection
|
|
33866
|
+
* @param prefix left marker (e.g. '**' for bold, '==' for highlight)
|
|
33867
|
+
* @param suffix right marker, defaults to prefix
|
|
33868
|
+
* @param placeholder text inserted when the selection is empty
|
|
33869
|
+
*/
|
|
33870
|
+
function wrapSelection(state, prefix, suffix = prefix, placeholder = WRAP_PLACEHOLDER) {
|
|
33871
|
+
const text = state?.text ?? '';
|
|
33872
|
+
let start = state?.selectionStart ?? 0;
|
|
33873
|
+
let end = state?.selectionEnd ?? 0;
|
|
33874
|
+
// Toggle off: selection directly surrounded by markers -> remove them
|
|
33875
|
+
if (start !== end && text.substring(Math.max(0, start - prefix.length), start) === prefix && text.substring(end, end + suffix.length) === suffix) {
|
|
33876
|
+
const before = text.substring(0, Math.max(0, start - prefix.length));
|
|
33877
|
+
const selection = text.substring(start, end);
|
|
33878
|
+
const after = text.substring(end + suffix.length);
|
|
33879
|
+
return {
|
|
33880
|
+
text: before + selection + after,
|
|
33881
|
+
selectionStart: start - prefix.length,
|
|
33882
|
+
selectionEnd: end - prefix.length,
|
|
33883
|
+
};
|
|
33884
|
+
}
|
|
33885
|
+
// Toggle off: selection itself contains the markers -> unwrap them
|
|
33886
|
+
let innerStart = start;
|
|
33887
|
+
let innerEnd = end;
|
|
33888
|
+
if (text.substring(start, Math.min(end, start + prefix.length)) === prefix && text.substring(Math.max(start, end - suffix.length), end) === suffix) {
|
|
33889
|
+
innerStart += prefix.length;
|
|
33890
|
+
innerEnd -= suffix.length;
|
|
33891
|
+
}
|
|
33892
|
+
const isEmptySelection = start === end;
|
|
33893
|
+
// When the markers are inside the selection (toggle off), they are removed:
|
|
33894
|
+
// - content before/after the selection keeps its original positions;
|
|
33895
|
+
// - the content starts at the original selection start.
|
|
33896
|
+
const didUnwrap = innerStart !== start;
|
|
33897
|
+
const before = text.substring(0, didUnwrap ? start : innerStart);
|
|
33898
|
+
const selection = text.substring(innerStart, innerEnd) || placeholder;
|
|
33899
|
+
// The trailing content continues after the original selection end (markers, if any, were inside it)
|
|
33900
|
+
const after = text.substring(end);
|
|
33901
|
+
// Caret right after the prefix marker; the selected text stays selected when wrapping existing text
|
|
33902
|
+
const selectionStart = didUnwrap ? start : start + prefix.length;
|
|
33903
|
+
const selectionEnd = didUnwrap ? start + selection.length : isEmptySelection ? selectionStart : selectionStart + selection.length;
|
|
33904
|
+
// Toggle off: the markers were inside the selection, just remove them
|
|
33905
|
+
const nextText = didUnwrap ? before + selection + after : before + prefix + selection + suffix + after;
|
|
33906
|
+
return {
|
|
33907
|
+
text: nextText,
|
|
33908
|
+
selectionStart,
|
|
33909
|
+
selectionEnd,
|
|
33910
|
+
};
|
|
33911
|
+
}
|
|
33912
|
+
/**
|
|
33913
|
+
* Toggles a line marker (e.g. '# ' for heading 1) on every involved line of the selection.
|
|
33914
|
+
* When all involved lines already have the marker, it is removed; another heading marker is replaced.
|
|
33915
|
+
*/
|
|
33916
|
+
function toggleLinePrefix(state, marker) {
|
|
33917
|
+
const text = state?.text ?? '';
|
|
33918
|
+
let start = state?.selectionStart ?? 0;
|
|
33919
|
+
let end = state?.selectionEnd ?? 0;
|
|
33920
|
+
// Line boundaries covering the selection
|
|
33921
|
+
const lineStart = text.lastIndexOf('\n', Math.max(0, start - 1)) + 1;
|
|
33922
|
+
const nextNewLine = text.indexOf('\n', end);
|
|
33923
|
+
const lineEnd = nextNewLine === -1 ? text.length : nextNewLine;
|
|
33924
|
+
const block = text.substring(lineStart, lineEnd);
|
|
33925
|
+
const lines = block.length ? block.split('\n') : [''];
|
|
33926
|
+
if (lines.every((line) => line.startsWith(marker))) {
|
|
33927
|
+
// Toggle off: remove the marker on each involved line
|
|
33928
|
+
return {
|
|
33929
|
+
text: text.substring(0, lineStart) + lines.map((line) => line.substring(marker.length)).join('\n') + text.substring(lineEnd),
|
|
33930
|
+
selectionStart: lineStart,
|
|
33931
|
+
selectionEnd: lineEnd - marker.length * lines.length,
|
|
33932
|
+
};
|
|
33933
|
+
}
|
|
33934
|
+
// Add (or replace an existing heading marker) on each involved line
|
|
33935
|
+
const replaceExisting = HEADING_MARKERS.filter((m) => m !== marker);
|
|
33936
|
+
const prefixed = lines
|
|
33937
|
+
.map((line) => {
|
|
33938
|
+
const existing = replaceExisting.find((m) => line.startsWith(m));
|
|
33939
|
+
return existing ? marker + line.substring(existing.length) : marker + line;
|
|
33940
|
+
})
|
|
33941
|
+
.join('\n');
|
|
33942
|
+
const diff = prefixed.length - block.length;
|
|
33943
|
+
return {
|
|
33944
|
+
text: text.substring(0, lineStart) + prefixed + text.substring(lineEnd),
|
|
33945
|
+
// Empty line(s): place the caret after the inserted marker; otherwise select the prefixed block
|
|
33946
|
+
selectionStart: block.length ? lineStart : lineStart + diff,
|
|
33947
|
+
selectionEnd: lineEnd + diff,
|
|
33948
|
+
};
|
|
33949
|
+
}
|
|
33950
|
+
/**
|
|
33951
|
+
* Inserts a markdown link: turns the selected text into a link label, or inserts a template
|
|
33952
|
+
* with the given placeholders when the selection is empty. The label stays selected afterwards.
|
|
33953
|
+
*/
|
|
33954
|
+
function insertLink(state, opts) {
|
|
33955
|
+
const text = state?.text ?? '';
|
|
33956
|
+
const start = Math.min(state?.selectionStart ?? 0, text.length);
|
|
33957
|
+
const end = Math.min(state?.selectionEnd ?? 0, text.length);
|
|
33958
|
+
const url = opts?.url ?? LINK_URL_PLACEHOLDER;
|
|
33959
|
+
const label = start === end ? (opts?.label ?? LINK_PLACEHOLDER) : text.substring(start, end);
|
|
33960
|
+
const link = `[${label}](${url})`;
|
|
33961
|
+
const before = text.substring(0, start);
|
|
33962
|
+
const after = text.substring(end);
|
|
33963
|
+
let labelStart;
|
|
33964
|
+
let labelEnd;
|
|
33965
|
+
if (start === end) {
|
|
33966
|
+
// Empty selection: select the default label, so the user can type over it
|
|
33967
|
+
labelStart = before.length + 1;
|
|
33968
|
+
labelEnd = labelStart + label.length;
|
|
33969
|
+
}
|
|
33970
|
+
else {
|
|
33971
|
+
// Reuse the existing text as label: keep it selected
|
|
33972
|
+
labelStart = start + 1;
|
|
33973
|
+
labelEnd = labelStart + label.length;
|
|
33974
|
+
}
|
|
33975
|
+
return {
|
|
33976
|
+
text: before + link + after,
|
|
33977
|
+
selectionStart: labelStart,
|
|
33978
|
+
selectionEnd: labelEnd,
|
|
33979
|
+
};
|
|
33980
|
+
}
|
|
33580
33981
|
|
|
33581
33982
|
class MarkdownService {
|
|
33582
33983
|
route = inject(ActivatedRoute);
|
|
@@ -34681,6 +35082,148 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34681
35082
|
}]
|
|
34682
35083
|
}] });
|
|
34683
35084
|
|
|
35085
|
+
/**
|
|
35086
|
+
* Marked inline extension rendering `==highlighted text==` as `<mark>` elements.
|
|
35087
|
+
*
|
|
35088
|
+
* The highlight syntax has no GFM standard: this extension completes the renderer
|
|
35089
|
+
* used by ngx-markdown, so composed messages (feed, inbox) render the same way as they are previewed.
|
|
35090
|
+
*
|
|
35091
|
+
* Registered through `SharedMarkdownModule.forRoot()` (`markedExtensions` option),
|
|
35092
|
+
* and testable without Angular by applying it on a `Marked` instance.
|
|
35093
|
+
*/
|
|
35094
|
+
const compositionMarkdownExtensions = [
|
|
35095
|
+
{
|
|
35096
|
+
extensions: [
|
|
35097
|
+
{
|
|
35098
|
+
name: 'highlight',
|
|
35099
|
+
level: 'inline',
|
|
35100
|
+
start(src) {
|
|
35101
|
+
const index = src.indexOf('==');
|
|
35102
|
+
return index === -1 ? undefined : index;
|
|
35103
|
+
},
|
|
35104
|
+
// Same-line content only, and no unbalanced side (e.g. 'a == b' stays as-is)
|
|
35105
|
+
tokenizer(src) {
|
|
35106
|
+
const match = /^==(?!\s)([^=\n]+?)(?<!\s)==/.exec(src);
|
|
35107
|
+
if (!match) {
|
|
35108
|
+
return undefined;
|
|
35109
|
+
}
|
|
35110
|
+
return {
|
|
35111
|
+
type: 'highlight',
|
|
35112
|
+
raw: match[0],
|
|
35113
|
+
text: match[1],
|
|
35114
|
+
// Keep nested inline syntaxes renderable (e.g. '==a **bold** b==')
|
|
35115
|
+
tokens: this.lexer.inlineTokens(match[1]),
|
|
35116
|
+
};
|
|
35117
|
+
},
|
|
35118
|
+
renderer(token) {
|
|
35119
|
+
return `<mark>${this.parser.parseInline(token.tokens ?? token.text)}</mark>`;
|
|
35120
|
+
},
|
|
35121
|
+
},
|
|
35122
|
+
],
|
|
35123
|
+
},
|
|
35124
|
+
];
|
|
35125
|
+
|
|
35126
|
+
/**
|
|
35127
|
+
* Markdown composition toolbar: a row of buttons that insert markdown syntax
|
|
35128
|
+
* at the caret of the linked textarea (bold, underline, strikethrough, highlight, headings, bullet list, link, attachment).
|
|
35129
|
+
*
|
|
35130
|
+
* Purely presentational towards the text: the component emits the new text through
|
|
35131
|
+
* {@link textChange}, so the host remains owner of the form control value.
|
|
35132
|
+
* The caret and selection are restored on the textarea after each action.
|
|
35133
|
+
*/
|
|
35134
|
+
class MarkdownToolbarComponent {
|
|
35135
|
+
/** The textarea to edit (native element). */
|
|
35136
|
+
textarea;
|
|
35137
|
+
/** Disable all buttons (e.g. when the host form is disabled). */
|
|
35138
|
+
disabled = false;
|
|
35139
|
+
/** Disable the edition buttons, but not the preview toggle (e.g. when the preview replaces the text). */
|
|
35140
|
+
editDisabled = false;
|
|
35141
|
+
/** Show the attachment (paper clip) button. */
|
|
35142
|
+
canAttach = false;
|
|
35143
|
+
/** Reflects the current preview state (highlight the toggle button when active). */
|
|
35144
|
+
preview = false;
|
|
35145
|
+
/** Emits the new text value after each insertion. */
|
|
35146
|
+
textChange = new EventEmitter();
|
|
35147
|
+
/** Emits when the attachment button is clicked. */
|
|
35148
|
+
attachClick = new EventEmitter();
|
|
35149
|
+
/** Emits when the preview toggle button is clicked. */
|
|
35150
|
+
previewToggle = new EventEmitter();
|
|
35151
|
+
bold() {
|
|
35152
|
+
this.apply(wrapSelection(this.currentState(), '**', '**'));
|
|
35153
|
+
}
|
|
35154
|
+
/** Underline: no markdown syntax exists, so use the inline HTML `<u>` tag (rendered by marked, and portable) */
|
|
35155
|
+
underline() {
|
|
35156
|
+
this.apply(wrapSelection(this.currentState(), '<u>', '</u>'));
|
|
35157
|
+
}
|
|
35158
|
+
/** Strikethrough (GFM syntax) */
|
|
35159
|
+
strikethrough() {
|
|
35160
|
+
this.apply(wrapSelection(this.currentState(), '~~', '~~'));
|
|
35161
|
+
}
|
|
35162
|
+
highlight() {
|
|
35163
|
+
this.apply(wrapSelection(this.currentState(), '==', '=='));
|
|
35164
|
+
}
|
|
35165
|
+
heading1() {
|
|
35166
|
+
this.apply(toggleLinePrefix(this.currentState(), '# '));
|
|
35167
|
+
}
|
|
35168
|
+
heading2() {
|
|
35169
|
+
this.apply(toggleLinePrefix(this.currentState(), '## '));
|
|
35170
|
+
}
|
|
35171
|
+
/** Bullet list: toggle the '- ' marker on each selected line */
|
|
35172
|
+
bulletList() {
|
|
35173
|
+
this.apply(toggleLinePrefix(this.currentState(), '- '));
|
|
35174
|
+
}
|
|
35175
|
+
link() {
|
|
35176
|
+
this.apply(insertLink(this.currentState()));
|
|
35177
|
+
}
|
|
35178
|
+
onAttachClick(event) {
|
|
35179
|
+
this.attachClick.emit(event);
|
|
35180
|
+
}
|
|
35181
|
+
togglePreview() {
|
|
35182
|
+
this.previewToggle.emit();
|
|
35183
|
+
}
|
|
35184
|
+
currentState() {
|
|
35185
|
+
const el = this.textarea;
|
|
35186
|
+
return {
|
|
35187
|
+
text: el?.value ?? '',
|
|
35188
|
+
selectionStart: el?.selectionStart ?? 0,
|
|
35189
|
+
selectionEnd: el?.selectionEnd ?? 0,
|
|
35190
|
+
};
|
|
35191
|
+
}
|
|
35192
|
+
apply(next) {
|
|
35193
|
+
if (!this.textarea) {
|
|
35194
|
+
return;
|
|
35195
|
+
}
|
|
35196
|
+
// Update the textarea value first, then the host's control value (synchronous),
|
|
35197
|
+
// then restore the caret, as updating the control may re-write the textarea value.
|
|
35198
|
+
this.textarea.value = next.text;
|
|
35199
|
+
this.textChange.emit(next.text);
|
|
35200
|
+
this.textarea.focus();
|
|
35201
|
+
this.textarea.setSelectionRange(next.selectionStart, next.selectionEnd);
|
|
35202
|
+
}
|
|
35203
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MarkdownToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
35204
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MarkdownToolbarComponent, isStandalone: false, selector: "app-markdown-toolbar", inputs: { textarea: "textarea", disabled: "disabled", editDisabled: "editDisabled", canAttach: "canAttach", preview: "preview" }, outputs: { textChange: "textChange", attachClick: "attachClick", previewToggle: "previewToggle" }, ngImport: i0, template: "<ion-toolbar role=\"toolbar\">\n <ion-buttons slot=\"start\">\n <!-- Bold -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"bold()\"\n [title]=\"'MARKDOWN.TOOLBAR.BOLD' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_bold</mat-icon>\n </ion-button>\n\n <!-- Underline (<u>text</u>) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"underline()\"\n [title]=\"'MARKDOWN.TOOLBAR.UNDERLINE' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_underlined</mat-icon>\n </ion-button>\n\n <!-- Strikethrough (~~text~~) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"strikethrough()\"\n [title]=\"'MARKDOWN.TOOLBAR.STRIKETHROUGH' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_strikethrough</mat-icon>\n </ion-button>\n\n <!-- Highlight (==text==) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"highlight()\"\n [title]=\"'MARKDOWN.TOOLBAR.HIGHLIGHT' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_color_text</mat-icon>\n </ion-button>\n\n <!-- Heading 1 -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"heading1()\"\n [title]=\"'MARKDOWN.TOOLBAR.HEADING1' | translate\"\n tabindex=\"-1\"\n >\n H1\n </ion-button>\n\n <!-- Heading 2 -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"heading2()\"\n [title]=\"'MARKDOWN.TOOLBAR.HEADING2' | translate\"\n tabindex=\"-1\"\n >\n H2\n </ion-button>\n\n <!-- Bullet list (- item) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"bulletList()\"\n [title]=\"'MARKDOWN.TOOLBAR.BULLET_LIST' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_list_bulleted</mat-icon>\n </ion-button>\n\n <!-- Link -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"link()\"\n [title]=\"'MARKDOWN.TOOLBAR.LINK' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">link</mat-icon>\n </ion-button>\n\n <!-- Attachment (paper clip) -->\n @if (canAttach) {\n <ion-button\n data-testid=\"attach-button\"\n [disabled]=\"disabled || editDisabled\"\n (click)=\"onAttachClick($event)\"\n [title]=\"'MARKDOWN.TOOLBAR.ATTACHMENT' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">attach_file</mat-icon>\n </ion-button>\n }\n </ion-buttons>\n\n <ion-buttons slot=\"end\">\n <!-- Preview toggle -->\n <ion-button\n data-testid=\"preview-button\"\n [disabled]=\"disabled\"\n [color]=\"preview ? 'primary' : undefined\"\n [class.active]=\"preview\"\n (click)=\"togglePreview()\"\n [title]=\"'MARKDOWN.TOOLBAR.PREVIEW' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">{{ preview ? 'visibility_off' : 'visibility' }}</mat-icon>\n </ion-button>\n </ion-buttons>\n</ion-toolbar>\n", styles: [":host{display:block}ion-toolbar{--min-height: 40px;--background: transparent}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
35205
|
+
}
|
|
35206
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MarkdownToolbarComponent, decorators: [{
|
|
35207
|
+
type: Component,
|
|
35208
|
+
args: [{ selector: 'app-markdown-toolbar', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-toolbar role=\"toolbar\">\n <ion-buttons slot=\"start\">\n <!-- Bold -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"bold()\"\n [title]=\"'MARKDOWN.TOOLBAR.BOLD' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_bold</mat-icon>\n </ion-button>\n\n <!-- Underline (<u>text</u>) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"underline()\"\n [title]=\"'MARKDOWN.TOOLBAR.UNDERLINE' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_underlined</mat-icon>\n </ion-button>\n\n <!-- Strikethrough (~~text~~) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"strikethrough()\"\n [title]=\"'MARKDOWN.TOOLBAR.STRIKETHROUGH' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_strikethrough</mat-icon>\n </ion-button>\n\n <!-- Highlight (==text==) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"highlight()\"\n [title]=\"'MARKDOWN.TOOLBAR.HIGHLIGHT' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_color_text</mat-icon>\n </ion-button>\n\n <!-- Heading 1 -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"heading1()\"\n [title]=\"'MARKDOWN.TOOLBAR.HEADING1' | translate\"\n tabindex=\"-1\"\n >\n H1\n </ion-button>\n\n <!-- Heading 2 -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"heading2()\"\n [title]=\"'MARKDOWN.TOOLBAR.HEADING2' | translate\"\n tabindex=\"-1\"\n >\n H2\n </ion-button>\n\n <!-- Bullet list (- item) -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"bulletList()\"\n [title]=\"'MARKDOWN.TOOLBAR.BULLET_LIST' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">format_list_bulleted</mat-icon>\n </ion-button>\n\n <!-- Link -->\n <ion-button\n [disabled]=\"disabled || editDisabled\"\n (click)=\"link()\"\n [title]=\"'MARKDOWN.TOOLBAR.LINK' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">link</mat-icon>\n </ion-button>\n\n <!-- Attachment (paper clip) -->\n @if (canAttach) {\n <ion-button\n data-testid=\"attach-button\"\n [disabled]=\"disabled || editDisabled\"\n (click)=\"onAttachClick($event)\"\n [title]=\"'MARKDOWN.TOOLBAR.ATTACHMENT' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">attach_file</mat-icon>\n </ion-button>\n }\n </ion-buttons>\n\n <ion-buttons slot=\"end\">\n <!-- Preview toggle -->\n <ion-button\n data-testid=\"preview-button\"\n [disabled]=\"disabled\"\n [color]=\"preview ? 'primary' : undefined\"\n [class.active]=\"preview\"\n (click)=\"togglePreview()\"\n [title]=\"'MARKDOWN.TOOLBAR.PREVIEW' | translate\"\n tabindex=\"-1\"\n >\n <mat-icon slot=\"icon-only\">{{ preview ? 'visibility_off' : 'visibility' }}</mat-icon>\n </ion-button>\n </ion-buttons>\n</ion-toolbar>\n", styles: [":host{display:block}ion-toolbar{--min-height: 40px;--background: transparent}\n"] }]
|
|
35209
|
+
}], propDecorators: { textarea: [{
|
|
35210
|
+
type: Input
|
|
35211
|
+
}], disabled: [{
|
|
35212
|
+
type: Input
|
|
35213
|
+
}], editDisabled: [{
|
|
35214
|
+
type: Input
|
|
35215
|
+
}], canAttach: [{
|
|
35216
|
+
type: Input
|
|
35217
|
+
}], preview: [{
|
|
35218
|
+
type: Input
|
|
35219
|
+
}], textChange: [{
|
|
35220
|
+
type: Output
|
|
35221
|
+
}], attachClick: [{
|
|
35222
|
+
type: Output
|
|
35223
|
+
}], previewToggle: [{
|
|
35224
|
+
type: Output
|
|
35225
|
+
}] } });
|
|
35226
|
+
|
|
34684
35227
|
// function that returns `MarkedOptions` with renderer override
|
|
34685
35228
|
class SharedMarkdownModule {
|
|
34686
35229
|
static forRoot() {
|
|
@@ -34695,6 +35238,12 @@ class SharedMarkdownModule {
|
|
|
34695
35238
|
provide: SANITIZE,
|
|
34696
35239
|
useValue: SecurityContext.NONE,
|
|
34697
35240
|
},
|
|
35241
|
+
// Composition extensions (e.g. ==highlight==), used by the markdown toolbar
|
|
35242
|
+
markedExtensions: compositionMarkdownExtensions.map((extension) => ({
|
|
35243
|
+
provide: MARKED_EXTENSIONS,
|
|
35244
|
+
useValue: extension,
|
|
35245
|
+
multi: true,
|
|
35246
|
+
})),
|
|
34698
35247
|
markedOptions: {
|
|
34699
35248
|
provide: MARKED_OPTIONS,
|
|
34700
35249
|
deps: [MarkdownService],
|
|
@@ -34719,15 +35268,18 @@ class SharedMarkdownModule {
|
|
|
34719
35268
|
// Component
|
|
34720
35269
|
AppMarkdownContent,
|
|
34721
35270
|
AppMarkdownModal,
|
|
35271
|
+
MarkdownToolbarComponent,
|
|
34722
35272
|
// Directive
|
|
34723
35273
|
MarkdownDirective], imports: [CommonModule,
|
|
34724
35274
|
SharedIonicModule,
|
|
34725
|
-
TranslatePipe,
|
|
35275
|
+
TranslatePipe,
|
|
35276
|
+
SharedMaterialModule, i4$3.MarkdownModule,
|
|
34726
35277
|
// App module
|
|
34727
35278
|
SharedToolbarModule,
|
|
34728
|
-
SharedDebugModule], exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective] });
|
|
35279
|
+
SharedDebugModule], exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective, MarkdownToolbarComponent] });
|
|
34729
35280
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedMarkdownModule, providers: [provideChildTranslateService()], imports: [CommonModule,
|
|
34730
35281
|
SharedIonicModule,
|
|
35282
|
+
SharedMaterialModule,
|
|
34731
35283
|
MarkdownModule.forChild(),
|
|
34732
35284
|
// App module
|
|
34733
35285
|
SharedToolbarModule,
|
|
@@ -34740,6 +35292,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34740
35292
|
CommonModule,
|
|
34741
35293
|
SharedIonicModule,
|
|
34742
35294
|
TranslatePipe,
|
|
35295
|
+
SharedMaterialModule,
|
|
34743
35296
|
MarkdownModule.forChild(),
|
|
34744
35297
|
// App module
|
|
34745
35298
|
SharedToolbarModule,
|
|
@@ -34749,10 +35302,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34749
35302
|
// Component
|
|
34750
35303
|
AppMarkdownContent,
|
|
34751
35304
|
AppMarkdownModal,
|
|
35305
|
+
MarkdownToolbarComponent,
|
|
34752
35306
|
// Directive
|
|
34753
35307
|
MarkdownDirective,
|
|
34754
35308
|
],
|
|
34755
|
-
exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective],
|
|
35309
|
+
exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective, MarkdownToolbarComponent],
|
|
34756
35310
|
providers: [provideChildTranslateService()],
|
|
34757
35311
|
}]
|
|
34758
35312
|
}] });
|
|
@@ -39498,6 +40052,7 @@ class MessageForm extends AppForm {
|
|
|
39498
40052
|
/**
|
|
39499
40053
|
* Allow the user to attach files to the message.
|
|
39500
40054
|
* When enabled, {@link uploadFn} and {@link deleteFn} should also be provided (see `UploadFileComponent`).
|
|
40055
|
+
* Also used by the markdown toolbar to show its attachment (paper clip) button.
|
|
39501
40056
|
*/
|
|
39502
40057
|
canAttach = false;
|
|
39503
40058
|
/** Upload callback, called for each added file (usually a call to the REST endpoint `POST api/upload`) */
|
|
@@ -39506,7 +40061,18 @@ class MessageForm extends AppForm {
|
|
|
39506
40061
|
deleteFn;
|
|
39507
40062
|
/** Max size of an attached file, in megabytes (default: 20 Mo) */
|
|
39508
40063
|
maxAttachmentSizeMb = 20;
|
|
40064
|
+
/**
|
|
40065
|
+
* Layout of the body preview: side by side with the body text (2 columns), or replacing the text (e.g. on small screens).
|
|
40066
|
+
* The edition toolbar buttons are disabled when the preview replaces the text.
|
|
40067
|
+
*/
|
|
40068
|
+
bodyPreviewSideBySide = false;
|
|
39509
40069
|
searchRecipients = new EventEmitter();
|
|
40070
|
+
/** Emits when the attachment button is clicked (after opening the file browser) */
|
|
40071
|
+
attach = new EventEmitter();
|
|
40072
|
+
/** Emits when the body preview is toggled (e.g. to let the parent modal grow, if side by side) */
|
|
40073
|
+
bodyPreviewChange = new EventEmitter();
|
|
40074
|
+
/** Whether the markdown preview of the body is displayed. */
|
|
40075
|
+
bodyPreview = false;
|
|
39510
40076
|
attachmentsUpload;
|
|
39511
40077
|
types = MessageTypeList;
|
|
39512
40078
|
/** Existing attachments of the message (already stored by the pod, e.g. when editing a feed item) */
|
|
@@ -39561,6 +40127,18 @@ class MessageForm extends AppForm {
|
|
|
39561
40127
|
isSamePerson(o1, o2) {
|
|
39562
40128
|
return EntityUtils.equals(o1, o2, 'id');
|
|
39563
40129
|
}
|
|
40130
|
+
/**
|
|
40131
|
+
* New body value, emitted by the markdown toolbar after an insertion at the caret.
|
|
40132
|
+
* The textarea value (and its caret) has already been updated by the toolbar.
|
|
40133
|
+
*/
|
|
40134
|
+
onBodyTextChange(value) {
|
|
40135
|
+
this.form.controls.body.setValue(value);
|
|
40136
|
+
}
|
|
40137
|
+
toggleBodyPreview() {
|
|
40138
|
+
this.bodyPreview = !this.bodyPreview;
|
|
40139
|
+
this.bodyPreviewChange.emit(this.bodyPreview);
|
|
40140
|
+
this.cd.markForCheck();
|
|
40141
|
+
}
|
|
39564
40142
|
setValue(data, opts) {
|
|
39565
40143
|
this.existingAttachments = (data?.attachments || []).filter((a) => isNotNil(a?.id));
|
|
39566
40144
|
this.uploadedAttachments = [];
|
|
@@ -39582,9 +40160,13 @@ class MessageForm extends AppForm {
|
|
|
39582
40160
|
/**
|
|
39583
40161
|
* Open the file browser, to add an attachment
|
|
39584
40162
|
*/
|
|
40163
|
+
/**
|
|
40164
|
+
* Open the file browser (called by the markdown toolbar attachment button), then emit {@link attach}
|
|
40165
|
+
*/
|
|
39585
40166
|
addAttachment(event) {
|
|
39586
40167
|
event?.preventDefault();
|
|
39587
40168
|
this.attachmentsUpload?.browse();
|
|
40169
|
+
this.attach.emit(event);
|
|
39588
40170
|
}
|
|
39589
40171
|
/**
|
|
39590
40172
|
* Highlight the form when a file is dragged over it
|
|
@@ -39702,11 +40284,11 @@ class MessageForm extends AppForm {
|
|
|
39702
40284
|
this.cd.markForCheck();
|
|
39703
40285
|
}
|
|
39704
40286
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageForm, deps: [{ token: i0.Injector }, { token: i1$2.UntypedFormBuilder }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
39705
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MessageForm, isStandalone: false, selector: "app-message-form", inputs: { mobile: "mobile", suggestFn: "suggestFn", subjectMinLength: "subjectMinLength", subjectMaxLength: "subjectMaxLength", bodyMaxLength: "bodyMaxLength", bodyAutoHeight: "bodyAutoHeight", canSelectType: "canSelectType", canRecipientFilter: "canRecipientFilter", recipientFilterCount: "recipientFilterCount", canSearchRecipients: "canSearchRecipients", canAttach: ["canAttach", "canAttach", toBoolean], uploadFn: "uploadFn", deleteFn: "deleteFn", maxAttachmentSizeMb: "maxAttachmentSizeMb" }, outputs: { searchRecipients: "searchRecipients" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" }, properties: { "class.file-over": "this.fileOver" } }, viewQueries: [{ propertyName: "attachmentsUpload", first: true, predicate: ["attachmentsUpload"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <ng-container *ngTemplateOutlet=\"debugPanel\"></ng-container>\n}\n\n<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n @if (canSelectType) {\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n }\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n class=\"ion-flex-auto\"\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n [style.flex-grow]=\"'1'\"\n >\n <!-- Fix #2090 (Lot 7): search button inside the field frame (as suffix), using the 'list' icon\n (ellipsis-horizontal is not the expected icon, see GitLab #2090) -->\n @if (canSearchRecipients) {\n <button\n matSuffix\n mat-icon-button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"searchRecipients.emit($event)\"\n [title]=\"'COMMON.BTN_SEARCH' | translate\"\n >\n <mat-icon>list</mat-icon>\n </button>\n }\n </mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n @if (form.controls.subject.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (form.controls.subject.hasError('minlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (form.controls.subject.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (subjectMaxLength) {\n <mat-hint align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n }\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n @if (form.controls.body.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n }\n @if (bodyMaxLength) {\n <mat-hint align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n }\n </mat-form-field>\n\n <!-- Attachments -->\n @if (canAttach) {\n <div class=\"message-attachments\">\n <ion-button\n class=\"add-attachment-button\"\n fill=\"clear\"\n size=\"small\"\n [disabled]=\"disabled\"\n (click)=\"addAttachment($event)\"\n >\n <mat-icon slot=\"start\">attach_file</mat-icon>\n <ion-label>{{ 'SOCIAL.MESSAGE.ATTACHMENTS.BTN_ADD' | translate }}</ion-label>\n </ion-button>\n\n <!-- existing attachments (already stored by the pod) -->\n @if (existingAttachments | isNotEmptyArray) {\n <ion-list class=\"existing-attachments\" lines=\"none\">\n @for (attachment of existingAttachments; track attachment.id) {\n <ion-item class=\"existing-attachment\">\n <mat-icon slot=\"start\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label>\n <h4>{{ attachment | map: getAttachmentName }}</h4>\n @if (attachment.size) {\n <p>{{ attachment.size | fileSize }}</p>\n }\n </ion-label>\n <button\n mat-icon-button\n slot=\"end\"\n class=\"remove-attachment\"\n [disabled]=\"disabled\"\n [title]=\"'SOCIAL.MESSAGE.ATTACHMENTS.BTN_REMOVE' | translate\"\n (click)=\"removeExistingAttachment($event, attachment)\"\n >\n <mat-icon>clear</mat-icon>\n </button>\n </ion-item>\n }\n </ion-list>\n }\n\n <!-- files list only (no drop area: files are added by the button above, or dropped anywhere in the form) -->\n <app-upload-file\n #attachmentsUpload\n [showDropArea]=\"false\"\n [instantUpload]=\"true\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxFileSizeMb]=\"maxAttachmentSizeMb\"\n (filesChange)=\"onAttachmentsChange($event)\"\n ></app-upload-file>\n @if (form.controls.attachments.getError('uploading')) {\n <ion-note color=\"danger\">\n {{ 'SOCIAL.MESSAGE.ATTACHMENTS.UPLOADING_ERROR' | translate }}\n </ion-note>\n }\n </div>\n }\n</form>\n\n<ng-template #debugPanel>\n <app-debug title=\"Message form\">\n <ion-grid>\n <ion-row>\n <ion-col>\n ready: {{ readySubject | async }}\n <br />\n loading: {{ loadingSubject | async }}\n <br />\n enabled: {{ enabled }}\n <br />\n dirty: {{ dirty }}\n <br />\n valid: {{ valid }}\n <br />\n <br />\n error: {{ _form | formError | json }}\n <br />\n recipients: {{ _form | formGetValue: 'recipients' | personToString }}\n <br />\n recipientFilter: {{ _form | formGetValue: 'recipientFilter' | json }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n</ng-template>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}.message-attachments{margin-top:8px}:host.file-over{display:block;outline:2px dashed var(--ion-color-accent);outline-offset:4px}\n"], dependencies: [{ kind: "directive", type: i2$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i1$3.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i9$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "showChips", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutoResizeDirective, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMaxRows", "cdkAutosizeMinRows"] }, { kind: "component", type: UploadFileComponent, selector: "app-upload-file", inputs: ["fileExtension", "fileExtensionsExcluded", "uniqueFile", "instantUpload", "uploadFn", "deleteFn", "maxParallelUpload", "maxFileSizeMb", "autoHideDropArea", "showDropArea", "showThumbnails"], outputs: ["filesChange"] }, { kind: "component", type: i11$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: i11$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: FormErrorPipe, name: "formError" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: MapPipe, name: "map" }, { kind: "pipe", type: PersonToStringPipe, name: "personToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
40287
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MessageForm, isStandalone: false, selector: "app-message-form", inputs: { mobile: "mobile", suggestFn: "suggestFn", subjectMinLength: "subjectMinLength", subjectMaxLength: "subjectMaxLength", bodyMaxLength: "bodyMaxLength", bodyAutoHeight: "bodyAutoHeight", canSelectType: "canSelectType", canRecipientFilter: "canRecipientFilter", recipientFilterCount: "recipientFilterCount", canSearchRecipients: "canSearchRecipients", canAttach: ["canAttach", "canAttach", toBoolean], uploadFn: "uploadFn", deleteFn: "deleteFn", maxAttachmentSizeMb: "maxAttachmentSizeMb", bodyPreviewSideBySide: ["bodyPreviewSideBySide", "bodyPreviewSideBySide", toBoolean] }, outputs: { searchRecipients: "searchRecipients", attach: "attach", bodyPreviewChange: "bodyPreviewChange" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" }, properties: { "class.file-over": "this.fileOver" } }, viewQueries: [{ propertyName: "attachmentsUpload", first: true, predicate: ["attachmentsUpload"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <ng-container *ngTemplateOutlet=\"debugPanel\"></ng-container>\n}\n\n<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n @if (canSelectType) {\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n }\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n class=\"ion-flex-auto\"\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n [style.flex-grow]=\"'1'\"\n >\n <!-- Fix #2090 (Lot 7): search button inside the field frame (as suffix), using the 'list' icon\n (ellipsis-horizontal is not the expected icon, see GitLab #2090) -->\n @if (canSearchRecipients) {\n <button\n matSuffix\n mat-icon-button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"searchRecipients.emit($event)\"\n [title]=\"'COMMON.BTN_SEARCH' | translate\"\n >\n <mat-icon>list</mat-icon>\n </button>\n }\n </mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n @if (form.controls.subject.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (form.controls.subject.hasError('minlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (form.controls.subject.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (subjectMaxLength) {\n <mat-hint align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n }\n </mat-form-field>\n\n <!-- Body -->\n <!-- Markdown composition toolbar (bold, highlight, headings, link, attachment) -->\n <app-markdown-toolbar\n [textarea]=\"bodyTextarea\"\n [canAttach]=\"canAttach\"\n [disabled]=\"disabled\"\n (attachClick)=\"addAttachment($event)\"\n (textChange)=\"onBodyTextChange($event)\"\n [preview]=\"bodyPreview\"\n [editDisabled]=\"bodyPreview && !bodyPreviewSideBySide\"\n (previewToggle)=\"toggleBodyPreview()\"\n ></app-markdown-toolbar>\n <!-- Body text and preview: side by side (2 columns), or the preview replaces the text -->\n <ion-row class=\"ion-no-padding\">\n <ion-col\n class=\"ion-no-padding\"\n [size]=\"bodyPreview && bodyPreviewSideBySide ? 6 : 12\"\n [hidden]=\"bodyPreview && !bodyPreviewSideBySide\"\n >\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n #bodyTextarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n @if (form.controls.body.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n }\n @if (bodyMaxLength) {\n <mat-hint align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength }\n }}\n </mat-hint>\n }\n </mat-form-field>\n </ion-col>\n\n <!-- Body preview (markdown rendering, toggled by the toolbar) -->\n @if (bodyPreview) {\n <ion-col\n class=\"ion-no-padding\"\n [size]=\"bodyPreviewSideBySide ? 6 : 12\"\n [class.ion-padding-start]=\"bodyPreviewSideBySide\"\n >\n <div class=\"body-preview\" data-testid=\"body-preview\">\n <markdown [data]=\"form.controls.body.value || ''\" emoji></markdown>\n </div>\n </ion-col>\n }\n </ion-row>\n\n <!-- Attachments -->\n @if (canAttach) {\n <div class=\"message-attachments\">\n <!-- existing attachments (already stored by the pod) -->\n @if (existingAttachments | isNotEmptyArray) {\n <ion-list class=\"existing-attachments\" lines=\"none\">\n @for (attachment of existingAttachments; track attachment.id) {\n <ion-item class=\"existing-attachment\">\n <mat-icon slot=\"start\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label>\n <h4>{{ attachment | map: getAttachmentName }}</h4>\n @if (attachment.size) {\n <p>{{ attachment.size | fileSize }}</p>\n }\n </ion-label>\n <button\n mat-icon-button\n slot=\"end\"\n class=\"remove-attachment\"\n [disabled]=\"disabled\"\n [title]=\"'SOCIAL.MESSAGE.ATTACHMENTS.BTN_REMOVE' | translate\"\n (click)=\"removeExistingAttachment($event, attachment)\"\n >\n <mat-icon>clear</mat-icon>\n </button>\n </ion-item>\n }\n </ion-list>\n }\n\n <!-- files list only (no drop area: files are added by the toolbar attachment button, or dropped anywhere in the form) -->\n <app-upload-file\n #attachmentsUpload\n [showDropArea]=\"false\"\n [instantUpload]=\"true\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxFileSizeMb]=\"maxAttachmentSizeMb\"\n (filesChange)=\"onAttachmentsChange($event)\"\n ></app-upload-file>\n @if (form.controls.attachments.getError('uploading')) {\n <ion-note color=\"danger\">\n {{ 'SOCIAL.MESSAGE.ATTACHMENTS.UPLOADING_ERROR' | translate }}\n </ion-note>\n }\n </div>\n }\n</form>\n\n<ng-template #debugPanel>\n <app-debug title=\"Message form\">\n <ion-grid>\n <ion-row>\n <ion-col>\n ready: {{ readySubject | async }}\n <br />\n loading: {{ loadingSubject | async }}\n <br />\n enabled: {{ enabled }}\n <br />\n dirty: {{ dirty }}\n <br />\n valid: {{ valid }}\n <br />\n <br />\n error: {{ _form | formError | json }}\n <br />\n recipients: {{ _form | formGetValue: 'recipients' | personToString }}\n <br />\n recipientFilter: {{ _form | formGetValue: 'recipientFilter' | json }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n</ng-template>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}app-markdown-toolbar{display:block;margin-bottom:4px}.body-preview{box-sizing:border-box;height:calc(100% - 22px);min-height:calc(11.5em + 32px);padding:0 1em;overflow:auto;border:1px dashed var(--ion-item-border-color, #c8cdd4);border-radius:4px}.body-preview>markdown{display:block}.message-attachments{margin-top:8px;--app-upload-file-list-max-height: none}:host.file-over{display:block;outline:2px dashed var(--ion-color-accent);outline-offset:4px}\n"], dependencies: [{ kind: "directive", type: i2$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i1$3.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i9$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "showChips", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutoResizeDirective, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMaxRows", "cdkAutosizeMinRows"] }, { kind: "component", type: UploadFileComponent, selector: "app-upload-file", inputs: ["fileExtension", "fileExtensionsExcluded", "uniqueFile", "instantUpload", "uploadFn", "deleteFn", "maxParallelUpload", "maxFileSizeMb", "autoHideDropArea", "showDropArea", "showThumbnails"], outputs: ["filesChange"] }, { kind: "component", type: i11$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: i11$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: MarkdownToolbarComponent, selector: "app-markdown-toolbar", inputs: ["textarea", "disabled", "editDisabled", "canAttach", "preview"], outputs: ["textChange", "attachClick", "previewToggle"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: FormErrorPipe, name: "formError" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: MapPipe, name: "map" }, { kind: "pipe", type: PersonToStringPipe, name: "personToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
39706
40288
|
}
|
|
39707
40289
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageForm, decorators: [{
|
|
39708
40290
|
type: Component,
|
|
39709
|
-
args: [{ selector: 'app-message-form', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- debug -->\n@if (debug) {\n <ng-container *ngTemplateOutlet=\"debugPanel\"></ng-container>\n}\n\n<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n @if (canSelectType) {\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n }\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n class=\"ion-flex-auto\"\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n [style.flex-grow]=\"'1'\"\n >\n <!-- Fix #2090 (Lot 7): search button inside the field frame (as suffix), using the 'list' icon\n (ellipsis-horizontal is not the expected icon, see GitLab #2090) -->\n @if (canSearchRecipients) {\n <button\n matSuffix\n mat-icon-button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"searchRecipients.emit($event)\"\n [title]=\"'COMMON.BTN_SEARCH' | translate\"\n >\n <mat-icon>list</mat-icon>\n </button>\n }\n </mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n @if (form.controls.subject.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (form.controls.subject.hasError('minlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (form.controls.subject.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (subjectMaxLength) {\n <mat-hint align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n }\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n
|
|
40291
|
+
args: [{ selector: 'app-message-form', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- debug -->\n@if (debug) {\n <ng-container *ngTemplateOutlet=\"debugPanel\"></ng-container>\n}\n\n<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n @if (canSelectType) {\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n }\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n class=\"ion-flex-auto\"\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n [style.flex-grow]=\"'1'\"\n >\n <!-- Fix #2090 (Lot 7): search button inside the field frame (as suffix), using the 'list' icon\n (ellipsis-horizontal is not the expected icon, see GitLab #2090) -->\n @if (canSearchRecipients) {\n <button\n matSuffix\n mat-icon-button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"searchRecipients.emit($event)\"\n [title]=\"'COMMON.BTN_SEARCH' | translate\"\n >\n <mat-icon>list</mat-icon>\n </button>\n }\n </mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n @if (form.controls.subject.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (form.controls.subject.hasError('minlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (form.controls.subject.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n }\n @if (subjectMaxLength) {\n <mat-hint align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n }\n </mat-form-field>\n\n <!-- Body -->\n <!-- Markdown composition toolbar (bold, highlight, headings, link, attachment) -->\n <app-markdown-toolbar\n [textarea]=\"bodyTextarea\"\n [canAttach]=\"canAttach\"\n [disabled]=\"disabled\"\n (attachClick)=\"addAttachment($event)\"\n (textChange)=\"onBodyTextChange($event)\"\n [preview]=\"bodyPreview\"\n [editDisabled]=\"bodyPreview && !bodyPreviewSideBySide\"\n (previewToggle)=\"toggleBodyPreview()\"\n ></app-markdown-toolbar>\n <!-- Body text and preview: side by side (2 columns), or the preview replaces the text -->\n <ion-row class=\"ion-no-padding\">\n <ion-col\n class=\"ion-no-padding\"\n [size]=\"bodyPreview && bodyPreviewSideBySide ? 6 : 12\"\n [hidden]=\"bodyPreview && !bodyPreviewSideBySide\"\n >\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n #bodyTextarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n @if (form.controls.body.hasError('maxlength')) {\n <mat-error>\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n }\n @if (bodyMaxLength) {\n <mat-hint align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength }\n }}\n </mat-hint>\n }\n </mat-form-field>\n </ion-col>\n\n <!-- Body preview (markdown rendering, toggled by the toolbar) -->\n @if (bodyPreview) {\n <ion-col\n class=\"ion-no-padding\"\n [size]=\"bodyPreviewSideBySide ? 6 : 12\"\n [class.ion-padding-start]=\"bodyPreviewSideBySide\"\n >\n <div class=\"body-preview\" data-testid=\"body-preview\">\n <markdown [data]=\"form.controls.body.value || ''\" emoji></markdown>\n </div>\n </ion-col>\n }\n </ion-row>\n\n <!-- Attachments -->\n @if (canAttach) {\n <div class=\"message-attachments\">\n <!-- existing attachments (already stored by the pod) -->\n @if (existingAttachments | isNotEmptyArray) {\n <ion-list class=\"existing-attachments\" lines=\"none\">\n @for (attachment of existingAttachments; track attachment.id) {\n <ion-item class=\"existing-attachment\">\n <mat-icon slot=\"start\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label>\n <h4>{{ attachment | map: getAttachmentName }}</h4>\n @if (attachment.size) {\n <p>{{ attachment.size | fileSize }}</p>\n }\n </ion-label>\n <button\n mat-icon-button\n slot=\"end\"\n class=\"remove-attachment\"\n [disabled]=\"disabled\"\n [title]=\"'SOCIAL.MESSAGE.ATTACHMENTS.BTN_REMOVE' | translate\"\n (click)=\"removeExistingAttachment($event, attachment)\"\n >\n <mat-icon>clear</mat-icon>\n </button>\n </ion-item>\n }\n </ion-list>\n }\n\n <!-- files list only (no drop area: files are added by the toolbar attachment button, or dropped anywhere in the form) -->\n <app-upload-file\n #attachmentsUpload\n [showDropArea]=\"false\"\n [instantUpload]=\"true\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxFileSizeMb]=\"maxAttachmentSizeMb\"\n (filesChange)=\"onAttachmentsChange($event)\"\n ></app-upload-file>\n @if (form.controls.attachments.getError('uploading')) {\n <ion-note color=\"danger\">\n {{ 'SOCIAL.MESSAGE.ATTACHMENTS.UPLOADING_ERROR' | translate }}\n </ion-note>\n }\n </div>\n }\n</form>\n\n<ng-template #debugPanel>\n <app-debug title=\"Message form\">\n <ion-grid>\n <ion-row>\n <ion-col>\n ready: {{ readySubject | async }}\n <br />\n loading: {{ loadingSubject | async }}\n <br />\n enabled: {{ enabled }}\n <br />\n dirty: {{ dirty }}\n <br />\n valid: {{ valid }}\n <br />\n <br />\n error: {{ _form | formError | json }}\n <br />\n recipients: {{ _form | formGetValue: 'recipients' | personToString }}\n <br />\n recipientFilter: {{ _form | formGetValue: 'recipientFilter' | json }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n</ng-template>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}app-markdown-toolbar{display:block;margin-bottom:4px}.body-preview{box-sizing:border-box;height:calc(100% - 22px);min-height:calc(11.5em + 32px);padding:0 1em;overflow:auto;border:1px dashed var(--ion-item-border-color, #c8cdd4);border-radius:4px}.body-preview>markdown{display:block}.message-attachments{margin-top:8px;--app-upload-file-list-max-height: none}:host.file-over{display:block;outline:2px dashed var(--ion-color-accent);outline-offset:4px}\n"] }]
|
|
39710
40292
|
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1$2.UntypedFormBuilder }, { type: i0.ChangeDetectorRef }], propDecorators: { mobile: [{
|
|
39711
40293
|
type: Input
|
|
39712
40294
|
}], suggestFn: [{
|
|
@@ -39736,8 +40318,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
39736
40318
|
type: Input
|
|
39737
40319
|
}], maxAttachmentSizeMb: [{
|
|
39738
40320
|
type: Input
|
|
40321
|
+
}], bodyPreviewSideBySide: [{
|
|
40322
|
+
type: Input,
|
|
40323
|
+
args: [{ transform: toBoolean }]
|
|
39739
40324
|
}], searchRecipients: [{
|
|
39740
40325
|
type: Output
|
|
40326
|
+
}], attach: [{
|
|
40327
|
+
type: Output
|
|
40328
|
+
}], bodyPreviewChange: [{
|
|
40329
|
+
type: Output
|
|
39741
40330
|
}], attachmentsUpload: [{
|
|
39742
40331
|
type: ViewChild,
|
|
39743
40332
|
args: ['attachmentsUpload']
|
|
@@ -39771,6 +40360,12 @@ class MessageModal {
|
|
|
39771
40360
|
accountService;
|
|
39772
40361
|
cd;
|
|
39773
40362
|
userController;
|
|
40363
|
+
/** Margin (in px) kept between the modal and the window borders, when the modal grows */
|
|
40364
|
+
static FIT_HEIGHT_MARGIN = 50;
|
|
40365
|
+
/** Min window width (in px) to display the body preview side by side with the text (2 columns), in a wider modal */
|
|
40366
|
+
static PREVIEW_SIDE_BY_SIDE_MIN_WINDOW_WIDTH = 1000;
|
|
40367
|
+
/** Max modal width (in px), when the body preview is displayed side by side */
|
|
40368
|
+
static PREVIEW_SIDE_BY_SIDE_MAX_WIDTH = 1200;
|
|
39774
40369
|
mobile;
|
|
39775
40370
|
debug = false;
|
|
39776
40371
|
title = 'SOCIAL.MESSAGE.NEW.TITLE';
|
|
@@ -39785,6 +40380,14 @@ class MessageModal {
|
|
|
39785
40380
|
deleteFn;
|
|
39786
40381
|
maxAttachmentSizeMb = 20;
|
|
39787
40382
|
form;
|
|
40383
|
+
elementRef = inject(ElementRef);
|
|
40384
|
+
/** Initial (default) height of the modal, used as the min height when fitting the content */
|
|
40385
|
+
initialHeight;
|
|
40386
|
+
/** Attachments count, at the last fit (used to allow the modal to shrink only when an attachment has been removed) */
|
|
40387
|
+
fittedAttachmentsCount = 0;
|
|
40388
|
+
resizeObserver;
|
|
40389
|
+
/** Display the body preview side by side with the text (if enough space), or replacing the text */
|
|
40390
|
+
bodyPreviewSideBySide = false;
|
|
39788
40391
|
constructor(settings, viewCtrl, accountService, cd, userController) {
|
|
39789
40392
|
this.settings = settings;
|
|
39790
40393
|
this.viewCtrl = viewCtrl;
|
|
@@ -39806,8 +40409,13 @@ class MessageModal {
|
|
|
39806
40409
|
}
|
|
39807
40410
|
this.form.markAsLoaded();
|
|
39808
40411
|
this.form.enable();
|
|
40412
|
+
// Grow the modal when attachments are added (or shrink back when removed)
|
|
40413
|
+
this.observeAttachmentsResize();
|
|
39809
40414
|
});
|
|
39810
40415
|
}
|
|
40416
|
+
ngOnDestroy() {
|
|
40417
|
+
this.resizeObserver?.disconnect();
|
|
40418
|
+
}
|
|
39811
40419
|
cancel() {
|
|
39812
40420
|
this.viewCtrl.dismiss();
|
|
39813
40421
|
}
|
|
@@ -39873,12 +40481,77 @@ class MessageModal {
|
|
|
39873
40481
|
markAsLoaded(opts) {
|
|
39874
40482
|
this.form.markAsLoaded(opts);
|
|
39875
40483
|
}
|
|
40484
|
+
/**
|
|
40485
|
+
* Observe the attachments area: when its size changes, fit the modal height to its content
|
|
40486
|
+
*/
|
|
40487
|
+
observeAttachmentsResize() {
|
|
40488
|
+
if (this.mobile || typeof ResizeObserver === 'undefined')
|
|
40489
|
+
return; // Skip on mobile (full screen modal)
|
|
40490
|
+
const attachmentsElement = this.elementRef.nativeElement.querySelector('.message-attachments');
|
|
40491
|
+
if (!attachmentsElement)
|
|
40492
|
+
return; // No attachments allowed
|
|
40493
|
+
this.resizeObserver = new ResizeObserver(() => this.fitHeightToContent());
|
|
40494
|
+
this.resizeObserver.observe(attachmentsElement);
|
|
40495
|
+
}
|
|
40496
|
+
/**
|
|
40497
|
+
* Fit the modal height to its content (to avoid any scroll), between its initial height,
|
|
40498
|
+
* and the window height (minus a margin). The modal never shrinks below its initial height.
|
|
40499
|
+
*
|
|
40500
|
+
* To avoid any flickering, the modal shrinks only when an attachment has been removed (or if `allowShrink`): the upload progress
|
|
40501
|
+
* (global and per file progress bars) makes the content grow temporarily, so the reached height is kept after the upload.
|
|
40502
|
+
*/
|
|
40503
|
+
fitHeightToContent(opts) {
|
|
40504
|
+
const page = this.elementRef.nativeElement; // The modal page (.ion-page), filling the modal
|
|
40505
|
+
const modal = page.closest('ion-modal');
|
|
40506
|
+
const content = page.querySelector('ion-content');
|
|
40507
|
+
const formElement = page.querySelector('app-message-form');
|
|
40508
|
+
if (!modal || !content || !formElement)
|
|
40509
|
+
return;
|
|
40510
|
+
const currentHeight = page.offsetHeight;
|
|
40511
|
+
this.initialHeight = this.initialHeight ?? currentHeight;
|
|
40512
|
+
const maxHeight = window.innerHeight - MessageModal.FIT_HEIGHT_MARGIN;
|
|
40513
|
+
if (this.initialHeight >= maxHeight)
|
|
40514
|
+
return; // Already full height: nothing to do
|
|
40515
|
+
// Required height = current height + content overflow (positive) or free space (negative)
|
|
40516
|
+
const contentPaddingBottom = parseFloat(getComputedStyle(content).getPropertyValue('--padding-bottom')) || 0;
|
|
40517
|
+
const delta = formElement.getBoundingClientRect().bottom + contentPaddingBottom - content.getBoundingClientRect().bottom;
|
|
40518
|
+
let height = Math.round(Math.max(this.initialHeight, Math.min(currentHeight + delta, maxHeight)));
|
|
40519
|
+
// Shrink only if an attachment has been removed
|
|
40520
|
+
const attachmentsCount = page.querySelectorAll('.message-attachments ion-item').length;
|
|
40521
|
+
if (height < currentHeight && !opts?.allowShrink && attachmentsCount >= this.fittedAttachmentsCount) {
|
|
40522
|
+
height = currentHeight;
|
|
40523
|
+
}
|
|
40524
|
+
this.fittedAttachmentsCount = attachmentsCount;
|
|
40525
|
+
if (Math.abs(height - currentHeight) >= 1) {
|
|
40526
|
+
modal.style.setProperty('--height', `${height}px`);
|
|
40527
|
+
}
|
|
40528
|
+
}
|
|
40529
|
+
/**
|
|
40530
|
+
* When the body preview is toggled: if enough space, widen the modal to display the preview side by side with the text;
|
|
40531
|
+
* otherwise (e.g. mobile, or small window), the preview replaces the text
|
|
40532
|
+
*/
|
|
40533
|
+
onBodyPreviewChange(preview) {
|
|
40534
|
+
const modal = this.elementRef.nativeElement.closest('ion-modal');
|
|
40535
|
+
if (preview) {
|
|
40536
|
+
this.bodyPreviewSideBySide = !this.mobile && window.innerWidth >= MessageModal.PREVIEW_SIDE_BY_SIDE_MIN_WINDOW_WIDTH;
|
|
40537
|
+
}
|
|
40538
|
+
if (preview && this.bodyPreviewSideBySide) {
|
|
40539
|
+
modal?.style.setProperty('--width', `min(${MessageModal.PREVIEW_SIDE_BY_SIDE_MAX_WIDTH}px, calc(100% - ${MessageModal.FIT_HEIGHT_MARGIN * 2}px))`);
|
|
40540
|
+
}
|
|
40541
|
+
else {
|
|
40542
|
+
modal?.style.removeProperty('--width');
|
|
40543
|
+
}
|
|
40544
|
+
this.markForCheck();
|
|
40545
|
+
// Fit the height to the new content (after the rendering)
|
|
40546
|
+
if (!this.mobile)
|
|
40547
|
+
setTimeout(() => this.fitHeightToContent({ allowShrink: true }));
|
|
40548
|
+
}
|
|
39876
40549
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageModal, deps: [{ token: LocalSettingsService }, { token: i2$2.ModalController }, { token: AccountService }, { token: i0.ChangeDetectorRef }, { token: USER_CONTROLLER, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
39877
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MessageModal, isStandalone: false, selector: "app-message-modal", inputs: { debug: ["debug", "debug", booleanAttribute], title: "title", suggestFn: "suggestFn", data: "data", canSelectType: ["canSelectType", "canSelectType", booleanAttribute], canRecipientFilter: ["canRecipientFilter", "canRecipientFilter", booleanAttribute], canSearchRecipients: ["canSearchRecipients", "canSearchRecipients", booleanAttribute], recipientFilterCount: ["recipientFilterCount", "recipientFilterCount", booleanAttribute], canAttach: ["canAttach", "canAttach", booleanAttribute], uploadFn: "uploadFn", deleteFn: "deleteFn", maxAttachmentSizeMb: "maxAttachmentSizeMb" }, viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, static: true }], ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-buttons slot=\"start\">\n <ion-button class=\"back-button\" (click)=\"cancel()\" visible-xs visible-sm visible-mobile>\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title>\n {{ title | translate }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"back-button\"\n (click)=\"doSubmit()\"\n [disabled]=\"!form.valid\"\n visible-xs\n visible-sm\n visible-mobile\n >\n <ion-icon slot=\"icon-only\" name=\"checkmark\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (form.error) {\n <ion-item visible-xs visible-sm visible-mobile lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"form.error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <app-message-form\n #form\n (onSubmit)=\"doSubmit()\"\n (onCancel)=\"cancel()\"\n [canSelectType]=\"canSelectType\"\n [canRecipientFilter]=\"canRecipientFilter\"\n [recipientFilterCount]=\"recipientFilterCount\"\n [suggestFn]=\"suggestFn\"\n [canSearchRecipients]=\"canSearchRecipients\"\n [canAttach]=\"canAttach\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxAttachmentSizeMb]=\"maxAttachmentSizeMb\"\n (searchRecipients)=\"openSelectUsers($event)\"\n [debug]=\"debug\"\n ></app-message-form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col></ion-col>\n\n <!-- buttons -->\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text>{{ 'COMMON.BTN_CANCEL' | translate }}</ion-text>\n </ion-button>\n\n <ion-button\n [fill]=\"form.invalid ? 'clear' : 'solid'\"\n [disabled]=\"form.loading || form.invalid\"\n (keyup.enter)=\"doSubmit()\"\n (click)=\"doSubmit()\"\n color=\"tertiary\"\n >\n <ion-text>{{ 'COMMON.BTN_SEND' | translate }}</ion-text>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: MessageForm, selector: "app-message-form", inputs: ["mobile", "suggestFn", "subjectMinLength", "subjectMaxLength", "bodyMaxLength", "bodyAutoHeight", "canSelectType", "canRecipientFilter", "recipientFilterCount", "canSearchRecipients", "canAttach", "uploadFn", "deleteFn", "maxAttachmentSizeMb"], outputs: ["searchRecipients"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
40550
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MessageModal, isStandalone: false, selector: "app-message-modal", inputs: { debug: ["debug", "debug", booleanAttribute], title: "title", suggestFn: "suggestFn", data: "data", canSelectType: ["canSelectType", "canSelectType", booleanAttribute], canRecipientFilter: ["canRecipientFilter", "canRecipientFilter", booleanAttribute], canSearchRecipients: ["canSearchRecipients", "canSearchRecipients", booleanAttribute], recipientFilterCount: ["recipientFilterCount", "recipientFilterCount", booleanAttribute], canAttach: ["canAttach", "canAttach", booleanAttribute], uploadFn: "uploadFn", deleteFn: "deleteFn", maxAttachmentSizeMb: "maxAttachmentSizeMb" }, viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, static: true }], ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-buttons slot=\"start\">\n <ion-button class=\"back-button\" (click)=\"cancel()\" visible-xs visible-sm visible-mobile>\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title>\n {{ title | translate }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"back-button\"\n (click)=\"doSubmit()\"\n [disabled]=\"!form.valid\"\n visible-xs\n visible-sm\n visible-mobile\n >\n <ion-icon slot=\"icon-only\" name=\"checkmark\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (form.error) {\n <ion-item visible-xs visible-sm visible-mobile lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"form.error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <app-message-form\n #form\n (onSubmit)=\"doSubmit()\"\n (onCancel)=\"cancel()\"\n [canSelectType]=\"canSelectType\"\n [canRecipientFilter]=\"canRecipientFilter\"\n [recipientFilterCount]=\"recipientFilterCount\"\n [suggestFn]=\"suggestFn\"\n [canSearchRecipients]=\"canSearchRecipients\"\n [canAttach]=\"canAttach\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxAttachmentSizeMb]=\"maxAttachmentSizeMb\"\n [bodyPreviewSideBySide]=\"bodyPreviewSideBySide\"\n (bodyPreviewChange)=\"onBodyPreviewChange($event)\"\n (searchRecipients)=\"openSelectUsers($event)\"\n [debug]=\"debug\"\n ></app-message-form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col></ion-col>\n\n <!-- buttons -->\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text>{{ 'COMMON.BTN_CANCEL' | translate }}</ion-text>\n </ion-button>\n\n <ion-button\n [fill]=\"form.invalid ? 'clear' : 'solid'\"\n [disabled]=\"form.loading || form.invalid\"\n (keyup.enter)=\"doSubmit()\"\n (click)=\"doSubmit()\"\n color=\"tertiary\"\n >\n <ion-text>{{ 'COMMON.BTN_SEND' | translate }}</ion-text>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: MessageForm, selector: "app-message-form", inputs: ["mobile", "suggestFn", "subjectMinLength", "subjectMaxLength", "bodyMaxLength", "bodyAutoHeight", "canSelectType", "canRecipientFilter", "recipientFilterCount", "canSearchRecipients", "canAttach", "uploadFn", "deleteFn", "maxAttachmentSizeMb", "bodyPreviewSideBySide"], outputs: ["searchRecipients", "attach", "bodyPreviewChange"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
39878
40551
|
}
|
|
39879
40552
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageModal, decorators: [{
|
|
39880
40553
|
type: Component,
|
|
39881
|
-
args: [{ selector: 'app-message-modal', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-buttons slot=\"start\">\n <ion-button class=\"back-button\" (click)=\"cancel()\" visible-xs visible-sm visible-mobile>\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title>\n {{ title | translate }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"back-button\"\n (click)=\"doSubmit()\"\n [disabled]=\"!form.valid\"\n visible-xs\n visible-sm\n visible-mobile\n >\n <ion-icon slot=\"icon-only\" name=\"checkmark\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (form.error) {\n <ion-item visible-xs visible-sm visible-mobile lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"form.error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <app-message-form\n #form\n (onSubmit)=\"doSubmit()\"\n (onCancel)=\"cancel()\"\n [canSelectType]=\"canSelectType\"\n [canRecipientFilter]=\"canRecipientFilter\"\n [recipientFilterCount]=\"recipientFilterCount\"\n [suggestFn]=\"suggestFn\"\n [canSearchRecipients]=\"canSearchRecipients\"\n [canAttach]=\"canAttach\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxAttachmentSizeMb]=\"maxAttachmentSizeMb\"\n (searchRecipients)=\"openSelectUsers($event)\"\n [debug]=\"debug\"\n ></app-message-form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col></ion-col>\n\n <!-- buttons -->\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text>{{ 'COMMON.BTN_CANCEL' | translate }}</ion-text>\n </ion-button>\n\n <ion-button\n [fill]=\"form.invalid ? 'clear' : 'solid'\"\n [disabled]=\"form.loading || form.invalid\"\n (keyup.enter)=\"doSubmit()\"\n (click)=\"doSubmit()\"\n color=\"tertiary\"\n >\n <ion-text>{{ 'COMMON.BTN_SEND' | translate }}</ion-text>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n" }]
|
|
40554
|
+
args: [{ selector: 'app-message-modal', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-buttons slot=\"start\">\n <ion-button class=\"back-button\" (click)=\"cancel()\" visible-xs visible-sm visible-mobile>\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title>\n {{ title | translate }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"back-button\"\n (click)=\"doSubmit()\"\n [disabled]=\"!form.valid\"\n visible-xs\n visible-sm\n visible-mobile\n >\n <ion-icon slot=\"icon-only\" name=\"checkmark\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (form.error) {\n <ion-item visible-xs visible-sm visible-mobile lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"form.error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <app-message-form\n #form\n (onSubmit)=\"doSubmit()\"\n (onCancel)=\"cancel()\"\n [canSelectType]=\"canSelectType\"\n [canRecipientFilter]=\"canRecipientFilter\"\n [recipientFilterCount]=\"recipientFilterCount\"\n [suggestFn]=\"suggestFn\"\n [canSearchRecipients]=\"canSearchRecipients\"\n [canAttach]=\"canAttach\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [maxAttachmentSizeMb]=\"maxAttachmentSizeMb\"\n [bodyPreviewSideBySide]=\"bodyPreviewSideBySide\"\n (bodyPreviewChange)=\"onBodyPreviewChange($event)\"\n (searchRecipients)=\"openSelectUsers($event)\"\n [debug]=\"debug\"\n ></app-message-form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col></ion-col>\n\n <!-- buttons -->\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-text>{{ 'COMMON.BTN_CANCEL' | translate }}</ion-text>\n </ion-button>\n\n <ion-button\n [fill]=\"form.invalid ? 'clear' : 'solid'\"\n [disabled]=\"form.loading || form.invalid\"\n (keyup.enter)=\"doSubmit()\"\n (click)=\"doSubmit()\"\n color=\"tertiary\"\n >\n <ion-text>{{ 'COMMON.BTN_SEND' | translate }}</ion-text>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n" }]
|
|
39882
40555
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i2$2.ModalController }, { type: AccountService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
|
|
39883
40556
|
type: Optional
|
|
39884
40557
|
}, {
|
|
@@ -44618,6 +45291,7 @@ class UsersPage extends AppTable {
|
|
|
44618
45291
|
this.debug = !environment.production;
|
|
44619
45292
|
}
|
|
44620
45293
|
filterExpansionPanel;
|
|
45294
|
+
filterAutocompleteFields;
|
|
44621
45295
|
get firstUserColumn() {
|
|
44622
45296
|
const columnName = this.displayedColumns[RESERVED_START_COLUMNS.length];
|
|
44623
45297
|
// Skip avatar column, if present
|
|
@@ -44728,6 +45402,9 @@ class UsersPage extends AppTable {
|
|
|
44728
45402
|
this.markAsLoading();
|
|
44729
45403
|
this.filterForm.reset();
|
|
44730
45404
|
this.setFilter(null);
|
|
45405
|
+
this.filterAutocompleteFields.forEach((field) => {
|
|
45406
|
+
field.reloadItems('');
|
|
45407
|
+
});
|
|
44731
45408
|
}
|
|
44732
45409
|
async restoreFilterOrLoad() {
|
|
44733
45410
|
this.markAsLoading();
|
|
@@ -44868,11 +45545,11 @@ class UsersPage extends AppTable {
|
|
|
44868
45545
|
getDisplayValueFn = FormFieldDefinitionUtils.getDisplayValueFn;
|
|
44869
45546
|
isNotNilOrBlank = isNotNilOrBlank;
|
|
44870
45547
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: UsersPage, deps: [{ token: i0.Injector }, { token: i1$2.UntypedFormBuilder }, { token: AccountService }, { token: i3$5.ValidatorService }, { token: ConfigService }, { token: APP_PERSON_SERVICE }, { token: MessageService }, { token: MenuService }, { token: ENVIRONMENT }], target: i0.ɵɵFactoryTarget.Component });
|
|
44871
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: UsersPage, isStandalone: false, selector: "app-users-table", inputs: { showSelectColumn: "showSelectColumn", showIdColumn: "showIdColumn", showUsernameColumn: "showUsernameColumn", showUsernameExtranetColumn: "showUsernameExtranetColumn", showEmailColumn: "showEmailColumn", showPubkeyColumn: "showPubkeyColumn", showCreationDateColumn: "showCreationDateColumn", showUpdateDateColumn: "showUpdateDateColumn", title: "title", toolbarColor: "toolbarColor", canSendMessage: ["canSendMessage", "canSendMessage", booleanAttribute], canEdit: ["canEdit", "canEdit", booleanAttribute], compact: ["compact", "compact", booleanAttribute], usePageSettings: ["usePageSettings", "usePageSettings", booleanAttribute], sticky: ["sticky", "sticky", booleanAttribute], stickyEnd: ["stickyEnd", "stickyEnd", booleanAttribute], canDownload: ["canDownload", "canDownload", booleanAttribute], inModal: ["inModal", "inModal", booleanAttribute], showFooter: ["showFooter", "showFooter", booleanAttribute], showToolbar: ["showToolbar", "showToolbar", booleanAttribute], showPaginator: ["showPaginator", "showPaginator", booleanAttribute] }, providers: [{ provide: ValidatorService, useExisting: PersonValidatorService }], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<app-toolbar\n [title]=\"title || 'USER.LIST.TITLE' | translate\"\n [color]=\"toolbarColor\"\n [canGoBack]=\"false\"\n [class.cdk-visible-hidden]=\"!showToolbar\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n>\n <ion-buttons slot=\"end\">\n <!-- Compose message -->\n @if (canSendMessage) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n (click)=\"openComposeMessageModal($event)\"\n >\n <ion-icon name=\"mail\" slot=\"icon-only\"></ion-icon>\n </button>\n }\n\n @if (!canEdit || (selection | isEmptySelection)) {\n @if (canEdit && !mobile) {\n <!-- Add -->\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- Refresh -->\n @if (!mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n (click)=\"emitRefresh()\"\n >\n <mat-icon>refresh</mat-icon>\n </button>\n }\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n } @else if (canEdit) {\n <!-- delete -->\n <button\n mat-icon-button\n class=\"hidden-xs hidden-sm\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n @if (!mobile && (canDownload || canDebug)) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"toolbarOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n\n<!-- Toolbar option menu -->\n<mat-menu #toolbarOptionsMenu=\"matMenu\" xPosition=\"after\">\n <ng-template matMenuContent>\n <!-- export button -->\n @if (canDownload) {\n <button mat-menu-item (click)=\"exportToCsv($event)\">\n <mat-icon>download</mat-icon>\n <ion-text>{{ 'FILE.CSV.BTN_DOWNLOAD_HELP' | translate }}</ion-text>\n </button>\n }\n\n <!-- Debug only: Test select users modal -->\n @if (canDebug) {\n <mat-divider></mat-divider>\n\n <button\n mat-menu-item\n #menuTrigger=\"matMenuTrigger\"\n (mouseenter)=\"menuTrigger.openMenu()\"\n [matMenuTriggerFor]=\"debugMenu\"\n >\n <mat-icon>\n <ion-icon name=\"bug\"></ion-icon>\n </mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_DEBUG_DOTS' | translate }}</ion-text>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<!-- debug menu -->\n<mat-menu #debugMenu=\"matMenu\">\n <ng-template matMenuContent>\n <button mat-menu-item (click)=\"devToggleDebug()\">\n <mat-icon>{{ debug ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_ENABLE_DEBUG' | translate }}</ion-text>\n </button>\n </ng-template>\n</mat-menu>\n\n<ion-content class=\"ion-no-padding\">\n <!-- error -->\n @let error = errorSubject | async;\n @if (mobile && (errorSubject | async)) {\n <ion-item lines=\"none\" class=\"hidden-sm hidden-xs hidden-mobile\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n <!-- debug -->\n @if (debug) {\n <app-debug>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n focusColumn: {{ focusColumn }}\n <br />\n loadingSubject: {{ loading }}\n <br />\n toolbarColor: {{ toolbarColor }}\n </ion-col>\n <ion-col>\n showPubkeyColumn: {{ showPubkeyColumn }}\n <br />\n <br />\n excludesColumns : {{ excludesColumns }}\n <br />\n displayedColumns: {{ displayedColumns }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n }\n\n <!-- search -->\n <mat-expansion-panel #filterExpansionPanel class=\"ion-no-padding filter-panel filter-panel-floating\">\n <form class=\"form-container ion-padding\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterForm($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search -->\n <mat-form-field>\n <mat-label>{{ 'USER.LIST.FILTER.SEARCH' | translate }}</mat-label>\n <input matInput formControlName=\"searchText\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- status -->\n <mat-form-field>\n <mat-label>{{ 'USER.STATUS' | translate }}</mat-label>\n <mat-select formControlName=\"statusId\">\n <mat-option [value]=\"null\">\n <i>\n <span>{{ 'COMMON.EMPTY_OPTION' | translate }}</span>\n </i>\n </mat-option>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.statusId)\"\n [hidden]=\"filterForm.controls.statusId.disabled || filterForm.controls.statusId.value == null\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- user profiles -->\n <mat-chips-field\n formControlName=\"userProfiles\"\n [placeholder]=\"'USER.PROFILES' | translate\"\n chipColor=\"accent\"\n [items]=\"userProfileList\"\n [displayAttributes]=\"['name']\"\n [clearable]=\"true\"\n ></mat-chips-field>\n </ion-col>\n </ion-row>\n\n <!-- Additional fields filters -->\n @if (additionalFilterFields | isNotEmptyArray) {\n <ion-row>\n @for (field of additionalFilterFields; track field.key) {\n <ion-col size=\"12\" size-md=\"6\" size-lg=\"4\">\n <app-form-field\n [definition]=\"field\"\n [formControlName]=\"field.key\"\n [clearable]=\"true\"\n floatLabel=\"auto\"\n ></app-form-field>\n </ion-col>\n }\n </ion-row>\n }\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) === true || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n <!-- Close panel -->\n <ion-button\n mat-button\n fill=\"clear\"\n color=\"dark\"\n (click)=\"filterExpansionPanel.close()\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- error -->\n @if (mobile && (errorSubject | async); as error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-text color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-text>\n </ion-item>\n }\n\n <div\n class=\"table-container\"\n [class.has-toolbar]=\"showToolbar\"\n [class.has-paginator]=\"showPaginator\"\n [class.has-form-buttons]=\"showFormButtons\"\n >\n <table\n #table\n mat-table\n matSort\n [class.compact]=\"compact\"\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear\n [trackBy]=\"trackByFn\"\n >\n <ng-container matColumnDef=\"select\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox\n (change)=\"$event ? masterToggle() : null\"\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox (click)=\"toggleSelectRow($event, row)\" [checked]=\"selection | isSelected: row\"></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id Column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-label>{{ row.currentData.id }}</ion-label>\n </td>\n </ng-container>\n\n <!-- avatar Column -->\n <ng-container matColumnDef=\"avatar\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-avatar [style.background-color]=\"'white'\">\n @if (row.currentData.avatar; as avatarUrl) {\n <ion-img [src]=\"avatarUrl\"></ion-img>\n } @else {\n <svg width=\"39\" height=\"39\" [data-jdenticon-value]=\"row.currentData.id\"></svg>\n }\n </ion-avatar>\n </td>\n </ng-container>\n\n <!-- lastName -->\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.LAST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'lastName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['lastName']\"\n [placeholder]=\"'USER.LAST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'lastName'\"\n />\n @if (row.validator.controls['lastName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['lastName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'lastName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- firstname -->\n <ng-container matColumnDef=\"firstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.FIRST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'firstName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['firstName']\"\n [placeholder]=\"'USER.FIRST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'firstName'\"\n />\n @if (row.validator.controls['firstName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['firstName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'firstName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- email -->\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.EMAIL' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'email'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['email']\"\n [placeholder]=\"'USER.EMAIL' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'email'\"\n />\n @if (row.validator.controls['email'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['email'].hasError('email')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_EMAIL' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'email' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- additional fields -->\n @for (definition of additionalFields; track definition.key) {\n <ng-container [matColumnDef]=\"definition.key\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ definition.label | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = definition.key\">\n @if (row.editing) {\n <app-form-field\n floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"row.validator.controls[definition.key]\"\n [required]=\"definition.extra?.users?.required\"\n [autofocus]=\"row.editing && focusColumn === definition.key\"\n ></app-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: definition.key | displayWith: getDisplayValueFn(definition) }}\n </ion-label>\n }\n </td>\n </ng-container>\n }\n\n <!-- profile column -->\n <ng-container matColumnDef=\"profile\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.PROFILE' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'profile'\">\n @if (row.editing) {\n <mat-form-field>\n <mat-select\n [formControl]=\"row.validator | formGetControl: 'mainProfile'\"\n [placeholder]=\"'USER.PROFILE' | translate\"\n >\n @for (item of profiles; track item) {\n <mat-option [value]=\"item\">\n {{ 'USER.PROFILE_ENUM.' + item | uppercase | translate }}\n </mat-option>\n }\n </mat-select>\n @if (row.validator.controls['mainProfile'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let profile = row.validator | formGetValue: 'mainProfile';\n @if (profile) {\n <ion-label appAutoTitle>{{ 'USER.PROFILE_ENUM.' + profile | uppercase | translate }}</ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.STATUS' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'status'\">\n @if (row.editing) {\n @let control = row.validator | formGetControl: 'statusId';\n <mat-form-field>\n <mat-select [formControl]=\"control\" [placeholder]=\"'REFERENTIAL.STATUS' | translate\">\n <mat-select-trigger>\n @if (control.value >= 0) {\n <span>\n {{ statusById[control.value]?.label | translate }}\n </span>\n }\n </mat-select-trigger>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <mat-icon>\n <ion-icon [name]=\"item.icon\"></ion-icon>\n </mat-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n @if (control.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let statusId = row.validator | formGetValue: 'statusId';\n @if (statusId >= 0) {\n <ion-label appAutoTitle>\n {{ (statusById | mapGet: statusId)?.label | translate }}\n </ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- username -->\n <ng-container matColumnDef=\"username\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'username'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.username\"\n [placeholder]=\"'USER.USERNAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'username'\"\n />\n @if (row.validator.controls.username.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'username' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- username extranet -->\n <ng-container matColumnDef=\"usernameExtranet\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME_EXTRANET' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'usernameExtranet'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.usernameExtranet\"\n [placeholder]=\"'USER.USERNAME_EXTRANET' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'usernameExtranet'\"\n />\n @if (row.validator.controls.usernameExtranet.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'usernameExtranet' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- pubkey -->\n <ng-container matColumnDef=\"pubkey\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.PUBKEY' | translate }}</span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n [title]=\"!showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n [matTooltip]=\"showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n (click)=\"focusColumn = 'pubkey'\"\n >\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['pubkey']\"\n [placeholder]=\"'USER.PUBKEY' | translate\"\n [readonly]=\"!row.editing\"\n autocomplete=\"off\"\n />\n @if (row.validator.controls.pubkey.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls.pubkey.hasError('pubkey')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_PUBKEY' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'pubkey' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Creation date column -->\n <ng-container matColumnDef=\"creationDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.CREATION_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.creationDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Update date column -->\n <ng-container matColumnDef=\"updateDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.UPDATE_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.updateDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmEditCreateClick)=\"confirmEditCreate($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [canCancel]=\"false\"\n >\n <!-- table options menu -->\n <button\n matHeader\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"tableOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-error]=\"row.validator.invalid\"\n [class.mat-row-dirty]=\"row.currentData.dirty\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-mdc-row-selected]=\"row.editing || (!canEdit && selection | isSelected: row)\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"!row.validator?.valid\"\n ></tr>\n </table>\n\n <!-- D\u00E9clencheur invisible pour le mat-menu -->\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n </div>\n</ion-content>\n\n<!-- Table options menu -->\n<mat-menu #tableOptionsMenu=\"matMenu\" xPosition=\"after\">\n <!-- display columns -->\n <button mat-menu-item (click)=\"openSelectColumnsModal($event)\">\n <mat-icon>view_column</mat-icon>\n <ion-label>{{ 'COMMON.DISPLAYED_COLUMNS_DOTS' | translate }}</ion-label>\n </button>\n\n @if (usePageSettings) {\n <mat-divider></mat-divider>\n\n <!-- Compact mode -->\n <button mat-menu-item (click)=\"toggleCompactMode()\">\n <mat-icon>{{ compact ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-label>{{ 'COMMON.BTN_COMPACT_ROWS' | translate }}</ion-label>\n </button>\n }\n</mat-menu>\n\n<ion-footer>\n @if (showPaginator) {\n <mat-paginator\n class=\"mat-paginator-footer\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (showFormButtons) {\n <app-form-buttons-bar\n (onCancel)=\"cancel($event)\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) === true || (dirtySubject | async) !== true\"\n >\n <!-- error -->\n @if (error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow()\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".mat-expansion-panel{margin-bottom:5px}.mat-expansion-panel .form-container mat-form-field{width:100%}.mat-expansion-panel mat-action-row ion-label{line-height:36px}ion-content{height:calc(100% - var(--table-offset, 0px))}.table-container{height:100%}.table-container.has-toolbar{--table-toolbar-height: var(--mat-toolbar-height, 64px)}.table-container.has-paginator{--table-paginator-height: var(--app-paginator-height, 34px)}.table-container.has-form-buttons{--form-buttons-height: var(--ion-toolbar-height, 56px)}.mat-mdc-table .mat-column-avatar{--avatar-width: 40px;--avatar-height: 40px;width:calc(var(--avatar-width) + 10px)}.mat-mdc-table .mat-column-avatar ion-avatar{--border-radius: 5px !important;--border-width: 1px !important;--border-color: rgba(var(--ion-color-secondary-rgb), .5) !important;height:var(--avatar-height);max-height:var(--avatar-height);width:var(--avatar-width);max-width:var(--avatar-width);border:solid var(--border-width) var(--border-color);margin:0 2px}.mat-mdc-table .mat-column-avatar ion-avatar svg{max-height:calc(var(--avatar-height) - 1px);max-width:calc(var(--avatar-width) - 1px)}.mat-mdc-table.compact{--mat-row-height: 33px}.mat-mdc-table.compact .mat-column-avatar{--avatar-width: calc(var(--mat-row-height, 33px) - 4px);--avatar-height: calc(var(--mat-row-height, 33px) - 4px)}.mat-mdc-table .mat-column-id{min-width:50px;max-width:120px;padding-right:var(--mat-cell-horizontal-padding, 4px)}.mat-mdc-table .mat-column-lastName,.mat-mdc-table .mat-column-firstName{min-width:80px}.mat-mdc-table .mat-column-email,.mat-mdc-table .mat-column-pubkey,.mat-mdc-table .mat-column-department{min-width:180px}.mat-mdc-table .mat-column-profile{min-width:110px}.mat-mdc-table .mat-column-status,.mat-mdc-table .mat-column-creationDate,.mat-mdc-table .mat-column-updateDate{min-width:130px}\n"], dependencies: [{ kind: "directive", type: i4$4.SvgJdenticonDirective, selector: "svg[data-jdenticon-hash],svg[data-jdenticon-value]", inputs: ["data-jdenticon-hash", "data-jdenticon-value", "width", "height"] }, { kind: "component", type: i2$2.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i8$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i8$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i8$1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i8$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i9$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: i9$1.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i9$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i9$2.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "showChips", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTitleDirective, selector: "ion-label[appAutoTitle], mat-label[appAutoTitle]", inputs: ["appAutoTitle"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: AppFormField, selector: "app-form-field", inputs: ["definition", "readonly", "disabled", "formControl", "formControlName", "placeholder", "compact", "required", "hideRequiredMarker", "floatLabel", "label", "appearance", "subscriptSizing", "tabindex", "autofocus", "clearable", "chipColor", "class", "debug", "panelClass", "panelWidth"], outputs: ["keyup.enter", "definitionChanged"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: ActionsColumnComponent, selector: "app-actions-column", inputs: ["matColumnDef", "style", "showPendingSpinner", "stickyEnd", "canCancel", "canConfirm", "canDelete", "canBackward", "canForward", "canConfirmAndAdd", "dirtyIcon", "optionsTitle", "class", "cellTemplateStart", "cellTemplate"], outputs: ["optionsClick", "cancelOrDeleteClick", "confirmEditCreateClick", "confirmAndAddClick", "backward", "forward"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: MapGetPipe, name: "mapGet" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: DisplayWithPipe, name: "displayWith" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }], animations: [slideUpDownAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
45548
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: UsersPage, isStandalone: false, selector: "app-users-table", inputs: { showSelectColumn: "showSelectColumn", showIdColumn: "showIdColumn", showUsernameColumn: "showUsernameColumn", showUsernameExtranetColumn: "showUsernameExtranetColumn", showEmailColumn: "showEmailColumn", showPubkeyColumn: "showPubkeyColumn", showCreationDateColumn: "showCreationDateColumn", showUpdateDateColumn: "showUpdateDateColumn", title: "title", toolbarColor: "toolbarColor", canSendMessage: ["canSendMessage", "canSendMessage", booleanAttribute], canEdit: ["canEdit", "canEdit", booleanAttribute], compact: ["compact", "compact", booleanAttribute], usePageSettings: ["usePageSettings", "usePageSettings", booleanAttribute], sticky: ["sticky", "sticky", booleanAttribute], stickyEnd: ["stickyEnd", "stickyEnd", booleanAttribute], canDownload: ["canDownload", "canDownload", booleanAttribute], inModal: ["inModal", "inModal", booleanAttribute], showFooter: ["showFooter", "showFooter", booleanAttribute], showToolbar: ["showToolbar", "showToolbar", booleanAttribute], showPaginator: ["showPaginator", "showPaginator", booleanAttribute] }, providers: [{ provide: ValidatorService, useExisting: PersonValidatorService }], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "filterAutocompleteFields", predicate: ["filterAutocompleteFields"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<app-toolbar\n [title]=\"title || 'USER.LIST.TITLE' | translate\"\n [color]=\"toolbarColor\"\n [canGoBack]=\"false\"\n [class.cdk-visible-hidden]=\"!showToolbar\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n>\n <ion-buttons slot=\"end\">\n <!-- Compose message -->\n @if (canSendMessage) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n (click)=\"openComposeMessageModal($event)\"\n >\n <ion-icon name=\"mail\" slot=\"icon-only\"></ion-icon>\n </button>\n }\n\n @if (!canEdit || (selection | isEmptySelection)) {\n @if (canEdit && !mobile) {\n <!-- Add -->\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- Refresh -->\n @if (!mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n (click)=\"emitRefresh()\"\n >\n <mat-icon>refresh</mat-icon>\n </button>\n }\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n } @else if (canEdit) {\n <!-- delete -->\n <button\n mat-icon-button\n class=\"hidden-xs hidden-sm\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n @if (!mobile && (canDownload || canDebug)) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"toolbarOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n\n<!-- Toolbar option menu -->\n<mat-menu #toolbarOptionsMenu=\"matMenu\" xPosition=\"after\">\n <ng-template matMenuContent>\n <!-- export button -->\n @if (canDownload) {\n <button mat-menu-item (click)=\"exportToCsv($event)\">\n <mat-icon>download</mat-icon>\n <ion-text>{{ 'FILE.CSV.BTN_DOWNLOAD_HELP' | translate }}</ion-text>\n </button>\n }\n\n <!-- Debug only: Test select users modal -->\n @if (canDebug) {\n <mat-divider></mat-divider>\n\n <button\n mat-menu-item\n #menuTrigger=\"matMenuTrigger\"\n (mouseenter)=\"menuTrigger.openMenu()\"\n [matMenuTriggerFor]=\"debugMenu\"\n >\n <mat-icon>\n <ion-icon name=\"bug\"></ion-icon>\n </mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_DEBUG_DOTS' | translate }}</ion-text>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<!-- debug menu -->\n<mat-menu #debugMenu=\"matMenu\">\n <ng-template matMenuContent>\n <button mat-menu-item (click)=\"devToggleDebug()\">\n <mat-icon>{{ debug ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_ENABLE_DEBUG' | translate }}</ion-text>\n </button>\n </ng-template>\n</mat-menu>\n\n<ion-content class=\"ion-no-padding\">\n <!-- error -->\n @let error = errorSubject | async;\n @if (mobile && (errorSubject | async)) {\n <ion-item lines=\"none\" class=\"hidden-sm hidden-xs hidden-mobile\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n <!-- debug -->\n @if (debug) {\n <app-debug>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n focusColumn: {{ focusColumn }}\n <br />\n loadingSubject: {{ loading }}\n <br />\n toolbarColor: {{ toolbarColor }}\n </ion-col>\n <ion-col>\n showPubkeyColumn: {{ showPubkeyColumn }}\n <br />\n <br />\n excludesColumns : {{ excludesColumns }}\n <br />\n displayedColumns: {{ displayedColumns }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n }\n\n <!-- search -->\n <mat-expansion-panel #filterExpansionPanel class=\"ion-no-padding filter-panel filter-panel-floating\">\n <form class=\"form-container ion-padding\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterForm($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search -->\n <mat-form-field>\n <mat-label>{{ 'USER.LIST.FILTER.SEARCH' | translate }}</mat-label>\n <input matInput formControlName=\"searchText\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- status -->\n <mat-form-field>\n <mat-label>{{ 'USER.STATUS' | translate }}</mat-label>\n <mat-select formControlName=\"statusId\">\n <mat-option [value]=\"null\">\n <i>\n <span>{{ 'COMMON.EMPTY_OPTION' | translate }}</span>\n </i>\n </mat-option>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.statusId)\"\n [hidden]=\"filterForm.controls.statusId.disabled || filterForm.controls.statusId.value == null\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- user profiles -->\n <mat-chips-field\n formControlName=\"userProfiles\"\n [placeholder]=\"'USER.PROFILES' | translate\"\n chipColor=\"accent\"\n [items]=\"userProfileList\"\n [displayAttributes]=\"['name']\"\n [clearable]=\"true\"\n ></mat-chips-field>\n </ion-col>\n </ion-row>\n\n <!-- Additional fields filters -->\n @if (additionalFilterFields | isNotEmptyArray) {\n <ion-row>\n @for (field of additionalFilterFields; track field.key) {\n <ion-col size=\"12\" size-md=\"6\" size-lg=\"4\">\n <app-form-field\n #filterAutocompleteFields\n [definition]=\"field\"\n [formControlName]=\"field.key\"\n [clearable]=\"true\"\n floatLabel=\"auto\"\n ></app-form-field>\n </ion-col>\n }\n </ion-row>\n }\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) === true || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n <!-- Close panel -->\n <ion-button\n mat-button\n fill=\"clear\"\n color=\"dark\"\n (click)=\"filterExpansionPanel.close()\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- error -->\n @if (mobile && (errorSubject | async); as error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-text color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-text>\n </ion-item>\n }\n\n <div\n class=\"table-container\"\n [class.has-toolbar]=\"showToolbar\"\n [class.has-paginator]=\"showPaginator\"\n [class.has-form-buttons]=\"showFormButtons\"\n >\n <table\n #table\n mat-table\n matSort\n [class.compact]=\"compact\"\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear\n [trackBy]=\"trackByFn\"\n >\n <ng-container matColumnDef=\"select\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox\n (change)=\"$event ? masterToggle() : null\"\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox (click)=\"toggleSelectRow($event, row)\" [checked]=\"selection | isSelected: row\"></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id Column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-label>{{ row.currentData.id }}</ion-label>\n </td>\n </ng-container>\n\n <!-- avatar Column -->\n <ng-container matColumnDef=\"avatar\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-avatar [style.background-color]=\"'white'\">\n @if (row.currentData.avatar; as avatarUrl) {\n <ion-img [src]=\"avatarUrl\"></ion-img>\n } @else {\n <svg width=\"39\" height=\"39\" [data-jdenticon-value]=\"row.currentData.id\"></svg>\n }\n </ion-avatar>\n </td>\n </ng-container>\n\n <!-- lastName -->\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.LAST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'lastName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['lastName']\"\n [placeholder]=\"'USER.LAST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'lastName'\"\n />\n @if (row.validator.controls['lastName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['lastName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'lastName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- firstname -->\n <ng-container matColumnDef=\"firstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.FIRST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'firstName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['firstName']\"\n [placeholder]=\"'USER.FIRST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'firstName'\"\n />\n @if (row.validator.controls['firstName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['firstName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'firstName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- email -->\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.EMAIL' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'email'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['email']\"\n [placeholder]=\"'USER.EMAIL' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'email'\"\n />\n @if (row.validator.controls['email'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['email'].hasError('email')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_EMAIL' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'email' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- additional fields -->\n @for (definition of additionalFields; track definition.key) {\n <ng-container [matColumnDef]=\"definition.key\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ definition.label | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = definition.key\">\n @if (row.editing) {\n <app-form-field\n floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"row.validator.controls[definition.key]\"\n [required]=\"definition.extra?.users?.required\"\n [autofocus]=\"row.editing && focusColumn === definition.key\"\n ></app-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: definition.key | displayWith: getDisplayValueFn(definition) }}\n </ion-label>\n }\n </td>\n </ng-container>\n }\n\n <!-- profile column -->\n <ng-container matColumnDef=\"profile\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.PROFILE' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'profile'\">\n @if (row.editing) {\n <mat-form-field>\n <mat-select\n [formControl]=\"row.validator | formGetControl: 'mainProfile'\"\n [placeholder]=\"'USER.PROFILE' | translate\"\n >\n @for (item of profiles; track item) {\n <mat-option [value]=\"item\">\n {{ 'USER.PROFILE_ENUM.' + item | uppercase | translate }}\n </mat-option>\n }\n </mat-select>\n @if (row.validator.controls['mainProfile'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let profile = row.validator | formGetValue: 'mainProfile';\n @if (profile) {\n <ion-label appAutoTitle>{{ 'USER.PROFILE_ENUM.' + profile | uppercase | translate }}</ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.STATUS' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'status'\">\n @if (row.editing) {\n @let control = row.validator | formGetControl: 'statusId';\n <mat-form-field>\n <mat-select [formControl]=\"control\" [placeholder]=\"'REFERENTIAL.STATUS' | translate\">\n <mat-select-trigger>\n @if (control.value >= 0) {\n <span>\n {{ statusById[control.value]?.label | translate }}\n </span>\n }\n </mat-select-trigger>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <mat-icon>\n <ion-icon [name]=\"item.icon\"></ion-icon>\n </mat-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n @if (control.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let statusId = row.validator | formGetValue: 'statusId';\n @if (statusId >= 0) {\n <ion-label appAutoTitle>\n {{ (statusById | mapGet: statusId)?.label | translate }}\n </ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- username -->\n <ng-container matColumnDef=\"username\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'username'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.username\"\n [placeholder]=\"'USER.USERNAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'username'\"\n />\n @if (row.validator.controls.username.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'username' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- username extranet -->\n <ng-container matColumnDef=\"usernameExtranet\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME_EXTRANET' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'usernameExtranet'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.usernameExtranet\"\n [placeholder]=\"'USER.USERNAME_EXTRANET' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'usernameExtranet'\"\n />\n @if (row.validator.controls.usernameExtranet.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'usernameExtranet' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- pubkey -->\n <ng-container matColumnDef=\"pubkey\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.PUBKEY' | translate }}</span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n [title]=\"!showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n [matTooltip]=\"showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n (click)=\"focusColumn = 'pubkey'\"\n >\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['pubkey']\"\n [placeholder]=\"'USER.PUBKEY' | translate\"\n [readonly]=\"!row.editing\"\n autocomplete=\"off\"\n />\n @if (row.validator.controls.pubkey.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls.pubkey.hasError('pubkey')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_PUBKEY' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'pubkey' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Creation date column -->\n <ng-container matColumnDef=\"creationDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.CREATION_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.creationDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Update date column -->\n <ng-container matColumnDef=\"updateDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.UPDATE_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.updateDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmEditCreateClick)=\"confirmEditCreate($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [canCancel]=\"false\"\n >\n <!-- table options menu -->\n <button\n matHeader\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"tableOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-error]=\"row.validator.invalid\"\n [class.mat-row-dirty]=\"row.currentData.dirty\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-mdc-row-selected]=\"row.editing || (!canEdit && selection | isSelected: row)\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"!row.validator?.valid\"\n ></tr>\n </table>\n\n <!-- D\u00E9clencheur invisible pour le mat-menu -->\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n </div>\n</ion-content>\n\n<!-- Table options menu -->\n<mat-menu #tableOptionsMenu=\"matMenu\" xPosition=\"after\">\n <!-- display columns -->\n <button mat-menu-item (click)=\"openSelectColumnsModal($event)\">\n <mat-icon>view_column</mat-icon>\n <ion-label>{{ 'COMMON.DISPLAYED_COLUMNS_DOTS' | translate }}</ion-label>\n </button>\n\n @if (usePageSettings) {\n <mat-divider></mat-divider>\n\n <!-- Compact mode -->\n <button mat-menu-item (click)=\"toggleCompactMode()\">\n <mat-icon>{{ compact ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-label>{{ 'COMMON.BTN_COMPACT_ROWS' | translate }}</ion-label>\n </button>\n }\n</mat-menu>\n\n<ion-footer>\n @if (showPaginator) {\n <mat-paginator\n class=\"mat-paginator-footer\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (showFormButtons) {\n <app-form-buttons-bar\n (onCancel)=\"cancel($event)\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) === true || (dirtySubject | async) !== true\"\n >\n <!-- error -->\n @if (error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow()\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".mat-expansion-panel{margin-bottom:5px}.mat-expansion-panel .form-container mat-form-field{width:100%}.mat-expansion-panel mat-action-row ion-label{line-height:36px}ion-content{height:calc(100% - var(--table-offset, 0px))}.table-container{height:100%}.table-container.has-toolbar{--table-toolbar-height: var(--mat-toolbar-height, 64px)}.table-container.has-paginator{--table-paginator-height: var(--app-paginator-height, 34px)}.table-container.has-form-buttons{--form-buttons-height: var(--ion-toolbar-height, 56px)}.mat-mdc-table .mat-column-avatar{--avatar-width: 40px;--avatar-height: 40px;width:calc(var(--avatar-width) + 10px)}.mat-mdc-table .mat-column-avatar ion-avatar{--border-radius: 5px !important;--border-width: 1px !important;--border-color: rgba(var(--ion-color-secondary-rgb), .5) !important;height:var(--avatar-height);max-height:var(--avatar-height);width:var(--avatar-width);max-width:var(--avatar-width);border:solid var(--border-width) var(--border-color);margin:0 2px}.mat-mdc-table .mat-column-avatar ion-avatar svg{max-height:calc(var(--avatar-height) - 1px);max-width:calc(var(--avatar-width) - 1px)}.mat-mdc-table.compact{--mat-row-height: 33px}.mat-mdc-table.compact .mat-column-avatar{--avatar-width: calc(var(--mat-row-height, 33px) - 4px);--avatar-height: calc(var(--mat-row-height, 33px) - 4px)}.mat-mdc-table .mat-column-id{min-width:50px;max-width:120px;padding-right:var(--mat-cell-horizontal-padding, 4px)}.mat-mdc-table .mat-column-lastName,.mat-mdc-table .mat-column-firstName{min-width:80px}.mat-mdc-table .mat-column-email,.mat-mdc-table .mat-column-pubkey,.mat-mdc-table .mat-column-department{min-width:180px}.mat-mdc-table .mat-column-profile{min-width:110px}.mat-mdc-table .mat-column-status,.mat-mdc-table .mat-column-creationDate,.mat-mdc-table .mat-column-updateDate{min-width:130px}\n"], dependencies: [{ kind: "directive", type: i4$4.SvgJdenticonDirective, selector: "svg[data-jdenticon-hash],svg[data-jdenticon-value]", inputs: ["data-jdenticon-hash", "data-jdenticon-value", "width", "height"] }, { kind: "component", type: i2$2.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i8$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i8$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i8$1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i8$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i9$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: i9$1.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i9$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i9$2.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "showChips", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTitleDirective, selector: "ion-label[appAutoTitle], mat-label[appAutoTitle]", inputs: ["appAutoTitle"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: AppFormField, selector: "app-form-field", inputs: ["definition", "readonly", "disabled", "formControl", "formControlName", "placeholder", "compact", "required", "hideRequiredMarker", "floatLabel", "label", "appearance", "subscriptSizing", "tabindex", "autofocus", "clearable", "chipColor", "class", "debug", "panelClass", "panelWidth"], outputs: ["keyup.enter", "definitionChanged"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: ActionsColumnComponent, selector: "app-actions-column", inputs: ["matColumnDef", "style", "showPendingSpinner", "stickyEnd", "canCancel", "canConfirm", "canDelete", "canBackward", "canForward", "canConfirmAndAdd", "dirtyIcon", "optionsTitle", "class", "cellTemplateStart", "cellTemplate"], outputs: ["optionsClick", "cancelOrDeleteClick", "confirmEditCreateClick", "confirmAndAddClick", "backward", "forward"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: MapGetPipe, name: "mapGet" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: DisplayWithPipe, name: "displayWith" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }], animations: [slideUpDownAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
44872
45549
|
}
|
|
44873
45550
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: UsersPage, decorators: [{
|
|
44874
45551
|
type: Component,
|
|
44875
|
-
args: [{ selector: 'app-users-table', providers: [{ provide: ValidatorService, useExisting: PersonValidatorService }], animations: [slideUpDownAnimation], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<app-toolbar\n [title]=\"title || 'USER.LIST.TITLE' | translate\"\n [color]=\"toolbarColor\"\n [canGoBack]=\"false\"\n [class.cdk-visible-hidden]=\"!showToolbar\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n>\n <ion-buttons slot=\"end\">\n <!-- Compose message -->\n @if (canSendMessage) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n (click)=\"openComposeMessageModal($event)\"\n >\n <ion-icon name=\"mail\" slot=\"icon-only\"></ion-icon>\n </button>\n }\n\n @if (!canEdit || (selection | isEmptySelection)) {\n @if (canEdit && !mobile) {\n <!-- Add -->\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- Refresh -->\n @if (!mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n (click)=\"emitRefresh()\"\n >\n <mat-icon>refresh</mat-icon>\n </button>\n }\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n } @else if (canEdit) {\n <!-- delete -->\n <button\n mat-icon-button\n class=\"hidden-xs hidden-sm\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n @if (!mobile && (canDownload || canDebug)) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"toolbarOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n\n<!-- Toolbar option menu -->\n<mat-menu #toolbarOptionsMenu=\"matMenu\" xPosition=\"after\">\n <ng-template matMenuContent>\n <!-- export button -->\n @if (canDownload) {\n <button mat-menu-item (click)=\"exportToCsv($event)\">\n <mat-icon>download</mat-icon>\n <ion-text>{{ 'FILE.CSV.BTN_DOWNLOAD_HELP' | translate }}</ion-text>\n </button>\n }\n\n <!-- Debug only: Test select users modal -->\n @if (canDebug) {\n <mat-divider></mat-divider>\n\n <button\n mat-menu-item\n #menuTrigger=\"matMenuTrigger\"\n (mouseenter)=\"menuTrigger.openMenu()\"\n [matMenuTriggerFor]=\"debugMenu\"\n >\n <mat-icon>\n <ion-icon name=\"bug\"></ion-icon>\n </mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_DEBUG_DOTS' | translate }}</ion-text>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<!-- debug menu -->\n<mat-menu #debugMenu=\"matMenu\">\n <ng-template matMenuContent>\n <button mat-menu-item (click)=\"devToggleDebug()\">\n <mat-icon>{{ debug ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_ENABLE_DEBUG' | translate }}</ion-text>\n </button>\n </ng-template>\n</mat-menu>\n\n<ion-content class=\"ion-no-padding\">\n <!-- error -->\n @let error = errorSubject | async;\n @if (mobile && (errorSubject | async)) {\n <ion-item lines=\"none\" class=\"hidden-sm hidden-xs hidden-mobile\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n <!-- debug -->\n @if (debug) {\n <app-debug>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n focusColumn: {{ focusColumn }}\n <br />\n loadingSubject: {{ loading }}\n <br />\n toolbarColor: {{ toolbarColor }}\n </ion-col>\n <ion-col>\n showPubkeyColumn: {{ showPubkeyColumn }}\n <br />\n <br />\n excludesColumns : {{ excludesColumns }}\n <br />\n displayedColumns: {{ displayedColumns }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n }\n\n <!-- search -->\n <mat-expansion-panel #filterExpansionPanel class=\"ion-no-padding filter-panel filter-panel-floating\">\n <form class=\"form-container ion-padding\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterForm($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search -->\n <mat-form-field>\n <mat-label>{{ 'USER.LIST.FILTER.SEARCH' | translate }}</mat-label>\n <input matInput formControlName=\"searchText\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- status -->\n <mat-form-field>\n <mat-label>{{ 'USER.STATUS' | translate }}</mat-label>\n <mat-select formControlName=\"statusId\">\n <mat-option [value]=\"null\">\n <i>\n <span>{{ 'COMMON.EMPTY_OPTION' | translate }}</span>\n </i>\n </mat-option>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.statusId)\"\n [hidden]=\"filterForm.controls.statusId.disabled || filterForm.controls.statusId.value == null\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- user profiles -->\n <mat-chips-field\n formControlName=\"userProfiles\"\n [placeholder]=\"'USER.PROFILES' | translate\"\n chipColor=\"accent\"\n [items]=\"userProfileList\"\n [displayAttributes]=\"['name']\"\n [clearable]=\"true\"\n ></mat-chips-field>\n </ion-col>\n </ion-row>\n\n <!-- Additional fields filters -->\n @if (additionalFilterFields | isNotEmptyArray) {\n <ion-row>\n @for (field of additionalFilterFields; track field.key) {\n <ion-col size=\"12\" size-md=\"6\" size-lg=\"4\">\n <app-form-field\n [definition]=\"field\"\n [formControlName]=\"field.key\"\n [clearable]=\"true\"\n floatLabel=\"auto\"\n ></app-form-field>\n </ion-col>\n }\n </ion-row>\n }\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) === true || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n <!-- Close panel -->\n <ion-button\n mat-button\n fill=\"clear\"\n color=\"dark\"\n (click)=\"filterExpansionPanel.close()\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- error -->\n @if (mobile && (errorSubject | async); as error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-text color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-text>\n </ion-item>\n }\n\n <div\n class=\"table-container\"\n [class.has-toolbar]=\"showToolbar\"\n [class.has-paginator]=\"showPaginator\"\n [class.has-form-buttons]=\"showFormButtons\"\n >\n <table\n #table\n mat-table\n matSort\n [class.compact]=\"compact\"\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear\n [trackBy]=\"trackByFn\"\n >\n <ng-container matColumnDef=\"select\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox\n (change)=\"$event ? masterToggle() : null\"\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox (click)=\"toggleSelectRow($event, row)\" [checked]=\"selection | isSelected: row\"></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id Column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-label>{{ row.currentData.id }}</ion-label>\n </td>\n </ng-container>\n\n <!-- avatar Column -->\n <ng-container matColumnDef=\"avatar\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-avatar [style.background-color]=\"'white'\">\n @if (row.currentData.avatar; as avatarUrl) {\n <ion-img [src]=\"avatarUrl\"></ion-img>\n } @else {\n <svg width=\"39\" height=\"39\" [data-jdenticon-value]=\"row.currentData.id\"></svg>\n }\n </ion-avatar>\n </td>\n </ng-container>\n\n <!-- lastName -->\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.LAST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'lastName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['lastName']\"\n [placeholder]=\"'USER.LAST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'lastName'\"\n />\n @if (row.validator.controls['lastName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['lastName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'lastName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- firstname -->\n <ng-container matColumnDef=\"firstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.FIRST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'firstName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['firstName']\"\n [placeholder]=\"'USER.FIRST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'firstName'\"\n />\n @if (row.validator.controls['firstName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['firstName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'firstName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- email -->\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.EMAIL' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'email'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['email']\"\n [placeholder]=\"'USER.EMAIL' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'email'\"\n />\n @if (row.validator.controls['email'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['email'].hasError('email')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_EMAIL' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'email' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- additional fields -->\n @for (definition of additionalFields; track definition.key) {\n <ng-container [matColumnDef]=\"definition.key\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ definition.label | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = definition.key\">\n @if (row.editing) {\n <app-form-field\n floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"row.validator.controls[definition.key]\"\n [required]=\"definition.extra?.users?.required\"\n [autofocus]=\"row.editing && focusColumn === definition.key\"\n ></app-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: definition.key | displayWith: getDisplayValueFn(definition) }}\n </ion-label>\n }\n </td>\n </ng-container>\n }\n\n <!-- profile column -->\n <ng-container matColumnDef=\"profile\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.PROFILE' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'profile'\">\n @if (row.editing) {\n <mat-form-field>\n <mat-select\n [formControl]=\"row.validator | formGetControl: 'mainProfile'\"\n [placeholder]=\"'USER.PROFILE' | translate\"\n >\n @for (item of profiles; track item) {\n <mat-option [value]=\"item\">\n {{ 'USER.PROFILE_ENUM.' + item | uppercase | translate }}\n </mat-option>\n }\n </mat-select>\n @if (row.validator.controls['mainProfile'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let profile = row.validator | formGetValue: 'mainProfile';\n @if (profile) {\n <ion-label appAutoTitle>{{ 'USER.PROFILE_ENUM.' + profile | uppercase | translate }}</ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.STATUS' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'status'\">\n @if (row.editing) {\n @let control = row.validator | formGetControl: 'statusId';\n <mat-form-field>\n <mat-select [formControl]=\"control\" [placeholder]=\"'REFERENTIAL.STATUS' | translate\">\n <mat-select-trigger>\n @if (control.value >= 0) {\n <span>\n {{ statusById[control.value]?.label | translate }}\n </span>\n }\n </mat-select-trigger>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <mat-icon>\n <ion-icon [name]=\"item.icon\"></ion-icon>\n </mat-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n @if (control.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let statusId = row.validator | formGetValue: 'statusId';\n @if (statusId >= 0) {\n <ion-label appAutoTitle>\n {{ (statusById | mapGet: statusId)?.label | translate }}\n </ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- username -->\n <ng-container matColumnDef=\"username\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'username'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.username\"\n [placeholder]=\"'USER.USERNAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'username'\"\n />\n @if (row.validator.controls.username.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'username' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- username extranet -->\n <ng-container matColumnDef=\"usernameExtranet\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME_EXTRANET' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'usernameExtranet'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.usernameExtranet\"\n [placeholder]=\"'USER.USERNAME_EXTRANET' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'usernameExtranet'\"\n />\n @if (row.validator.controls.usernameExtranet.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'usernameExtranet' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- pubkey -->\n <ng-container matColumnDef=\"pubkey\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.PUBKEY' | translate }}</span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n [title]=\"!showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n [matTooltip]=\"showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n (click)=\"focusColumn = 'pubkey'\"\n >\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['pubkey']\"\n [placeholder]=\"'USER.PUBKEY' | translate\"\n [readonly]=\"!row.editing\"\n autocomplete=\"off\"\n />\n @if (row.validator.controls.pubkey.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls.pubkey.hasError('pubkey')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_PUBKEY' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'pubkey' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Creation date column -->\n <ng-container matColumnDef=\"creationDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.CREATION_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.creationDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Update date column -->\n <ng-container matColumnDef=\"updateDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.UPDATE_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.updateDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmEditCreateClick)=\"confirmEditCreate($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [canCancel]=\"false\"\n >\n <!-- table options menu -->\n <button\n matHeader\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"tableOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-error]=\"row.validator.invalid\"\n [class.mat-row-dirty]=\"row.currentData.dirty\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-mdc-row-selected]=\"row.editing || (!canEdit && selection | isSelected: row)\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"!row.validator?.valid\"\n ></tr>\n </table>\n\n <!-- D\u00E9clencheur invisible pour le mat-menu -->\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n </div>\n</ion-content>\n\n<!-- Table options menu -->\n<mat-menu #tableOptionsMenu=\"matMenu\" xPosition=\"after\">\n <!-- display columns -->\n <button mat-menu-item (click)=\"openSelectColumnsModal($event)\">\n <mat-icon>view_column</mat-icon>\n <ion-label>{{ 'COMMON.DISPLAYED_COLUMNS_DOTS' | translate }}</ion-label>\n </button>\n\n @if (usePageSettings) {\n <mat-divider></mat-divider>\n\n <!-- Compact mode -->\n <button mat-menu-item (click)=\"toggleCompactMode()\">\n <mat-icon>{{ compact ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-label>{{ 'COMMON.BTN_COMPACT_ROWS' | translate }}</ion-label>\n </button>\n }\n</mat-menu>\n\n<ion-footer>\n @if (showPaginator) {\n <mat-paginator\n class=\"mat-paginator-footer\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (showFormButtons) {\n <app-form-buttons-bar\n (onCancel)=\"cancel($event)\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) === true || (dirtySubject | async) !== true\"\n >\n <!-- error -->\n @if (error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow()\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".mat-expansion-panel{margin-bottom:5px}.mat-expansion-panel .form-container mat-form-field{width:100%}.mat-expansion-panel mat-action-row ion-label{line-height:36px}ion-content{height:calc(100% - var(--table-offset, 0px))}.table-container{height:100%}.table-container.has-toolbar{--table-toolbar-height: var(--mat-toolbar-height, 64px)}.table-container.has-paginator{--table-paginator-height: var(--app-paginator-height, 34px)}.table-container.has-form-buttons{--form-buttons-height: var(--ion-toolbar-height, 56px)}.mat-mdc-table .mat-column-avatar{--avatar-width: 40px;--avatar-height: 40px;width:calc(var(--avatar-width) + 10px)}.mat-mdc-table .mat-column-avatar ion-avatar{--border-radius: 5px !important;--border-width: 1px !important;--border-color: rgba(var(--ion-color-secondary-rgb), .5) !important;height:var(--avatar-height);max-height:var(--avatar-height);width:var(--avatar-width);max-width:var(--avatar-width);border:solid var(--border-width) var(--border-color);margin:0 2px}.mat-mdc-table .mat-column-avatar ion-avatar svg{max-height:calc(var(--avatar-height) - 1px);max-width:calc(var(--avatar-width) - 1px)}.mat-mdc-table.compact{--mat-row-height: 33px}.mat-mdc-table.compact .mat-column-avatar{--avatar-width: calc(var(--mat-row-height, 33px) - 4px);--avatar-height: calc(var(--mat-row-height, 33px) - 4px)}.mat-mdc-table .mat-column-id{min-width:50px;max-width:120px;padding-right:var(--mat-cell-horizontal-padding, 4px)}.mat-mdc-table .mat-column-lastName,.mat-mdc-table .mat-column-firstName{min-width:80px}.mat-mdc-table .mat-column-email,.mat-mdc-table .mat-column-pubkey,.mat-mdc-table .mat-column-department{min-width:180px}.mat-mdc-table .mat-column-profile{min-width:110px}.mat-mdc-table .mat-column-status,.mat-mdc-table .mat-column-creationDate,.mat-mdc-table .mat-column-updateDate{min-width:130px}\n"] }]
|
|
45552
|
+
args: [{ selector: 'app-users-table', providers: [{ provide: ValidatorService, useExisting: PersonValidatorService }], animations: [slideUpDownAnimation], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<app-toolbar\n [title]=\"title || 'USER.LIST.TITLE' | translate\"\n [color]=\"toolbarColor\"\n [canGoBack]=\"false\"\n [class.cdk-visible-hidden]=\"!showToolbar\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n>\n <ion-buttons slot=\"end\">\n <!-- Compose message -->\n @if (canSendMessage) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('USER.LIST.BTN_SEND_MESSAGE' | translate) : ''\"\n (click)=\"openComposeMessageModal($event)\"\n >\n <ion-icon name=\"mail\" slot=\"icon-only\"></ion-icon>\n </button>\n }\n\n @if (!canEdit || (selection | isEmptySelection)) {\n @if (canEdit && !mobile) {\n <!-- Add -->\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- Refresh -->\n @if (!mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_REFRESH' | translate) : ''\"\n (click)=\"emitRefresh()\"\n >\n <mat-icon>refresh</mat-icon>\n </button>\n }\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n } @else if (canEdit) {\n <!-- delete -->\n <button\n mat-icon-button\n class=\"hidden-xs hidden-sm\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n @if (!mobile && (canDownload || canDebug)) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"toolbarOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n\n<!-- Toolbar option menu -->\n<mat-menu #toolbarOptionsMenu=\"matMenu\" xPosition=\"after\">\n <ng-template matMenuContent>\n <!-- export button -->\n @if (canDownload) {\n <button mat-menu-item (click)=\"exportToCsv($event)\">\n <mat-icon>download</mat-icon>\n <ion-text>{{ 'FILE.CSV.BTN_DOWNLOAD_HELP' | translate }}</ion-text>\n </button>\n }\n\n <!-- Debug only: Test select users modal -->\n @if (canDebug) {\n <mat-divider></mat-divider>\n\n <button\n mat-menu-item\n #menuTrigger=\"matMenuTrigger\"\n (mouseenter)=\"menuTrigger.openMenu()\"\n [matMenuTriggerFor]=\"debugMenu\"\n >\n <mat-icon>\n <ion-icon name=\"bug\"></ion-icon>\n </mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_DEBUG_DOTS' | translate }}</ion-text>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<!-- debug menu -->\n<mat-menu #debugMenu=\"matMenu\">\n <ng-template matMenuContent>\n <button mat-menu-item (click)=\"devToggleDebug()\">\n <mat-icon>{{ debug ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-text>{{ 'COMMON.DEBUG.BTN_ENABLE_DEBUG' | translate }}</ion-text>\n </button>\n </ng-template>\n</mat-menu>\n\n<ion-content class=\"ion-no-padding\">\n <!-- error -->\n @let error = errorSubject | async;\n @if (mobile && (errorSubject | async)) {\n <ion-item lines=\"none\" class=\"hidden-sm hidden-xs hidden-mobile\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n <!-- debug -->\n @if (debug) {\n <app-debug>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n focusColumn: {{ focusColumn }}\n <br />\n loadingSubject: {{ loading }}\n <br />\n toolbarColor: {{ toolbarColor }}\n </ion-col>\n <ion-col>\n showPubkeyColumn: {{ showPubkeyColumn }}\n <br />\n <br />\n excludesColumns : {{ excludesColumns }}\n <br />\n displayedColumns: {{ displayedColumns }}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n }\n\n <!-- search -->\n <mat-expansion-panel #filterExpansionPanel class=\"ion-no-padding filter-panel filter-panel-floating\">\n <form class=\"form-container ion-padding\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterForm($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search -->\n <mat-form-field>\n <mat-label>{{ 'USER.LIST.FILTER.SEARCH' | translate }}</mat-label>\n <input matInput formControlName=\"searchText\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- status -->\n <mat-form-field>\n <mat-label>{{ 'USER.STATUS' | translate }}</mat-label>\n <mat-select formControlName=\"statusId\">\n <mat-option [value]=\"null\">\n <i>\n <span>{{ 'COMMON.EMPTY_OPTION' | translate }}</span>\n </i>\n </mat-option>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.statusId)\"\n [hidden]=\"filterForm.controls.statusId.disabled || filterForm.controls.statusId.value == null\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- user profiles -->\n <mat-chips-field\n formControlName=\"userProfiles\"\n [placeholder]=\"'USER.PROFILES' | translate\"\n chipColor=\"accent\"\n [items]=\"userProfileList\"\n [displayAttributes]=\"['name']\"\n [clearable]=\"true\"\n ></mat-chips-field>\n </ion-col>\n </ion-row>\n\n <!-- Additional fields filters -->\n @if (additionalFilterFields | isNotEmptyArray) {\n <ion-row>\n @for (field of additionalFilterFields; track field.key) {\n <ion-col size=\"12\" size-md=\"6\" size-lg=\"4\">\n <app-form-field\n #filterAutocompleteFields\n [definition]=\"field\"\n [formControlName]=\"field.key\"\n [clearable]=\"true\"\n floatLabel=\"auto\"\n ></app-form-field>\n </ion-col>\n }\n </ion-row>\n }\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) === true || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n <!-- Close panel -->\n <ion-button\n mat-button\n fill=\"clear\"\n color=\"dark\"\n (click)=\"filterExpansionPanel.close()\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- error -->\n @if (mobile && (errorSubject | async); as error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-text color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-text>\n </ion-item>\n }\n\n <div\n class=\"table-container\"\n [class.has-toolbar]=\"showToolbar\"\n [class.has-paginator]=\"showPaginator\"\n [class.has-form-buttons]=\"showFormButtons\"\n >\n <table\n #table\n mat-table\n matSort\n [class.compact]=\"compact\"\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear\n [trackBy]=\"trackByFn\"\n >\n <ng-container matColumnDef=\"select\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox\n (change)=\"$event ? masterToggle() : null\"\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!inlineEdition && !showSelectColumn\">\n <mat-checkbox (click)=\"toggleSelectRow($event, row)\" [checked]=\"selection | isSelected: row\"></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id Column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-label>{{ row.currentData.id }}</ion-label>\n </td>\n </ng-container>\n\n <!-- avatar Column -->\n <ng-container matColumnDef=\"avatar\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let row\">\n <ion-avatar [style.background-color]=\"'white'\">\n @if (row.currentData.avatar; as avatarUrl) {\n <ion-img [src]=\"avatarUrl\"></ion-img>\n } @else {\n <svg width=\"39\" height=\"39\" [data-jdenticon-value]=\"row.currentData.id\"></svg>\n }\n </ion-avatar>\n </td>\n </ng-container>\n\n <!-- lastName -->\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.LAST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'lastName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['lastName']\"\n [placeholder]=\"'USER.LAST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'lastName'\"\n />\n @if (row.validator.controls['lastName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['lastName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'lastName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- firstname -->\n <ng-container matColumnDef=\"firstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.FIRST_NAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'firstName'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['firstName']\"\n [placeholder]=\"'USER.FIRST_NAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'firstName'\"\n />\n @if (row.validator.controls['firstName'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['firstName'].hasError('minlength')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_MIN_LENGTH' | translate: { requiredLength: 2 } }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'firstName' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- email -->\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.EMAIL' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'email'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['email']\"\n [placeholder]=\"'USER.EMAIL' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'email'\"\n />\n @if (row.validator.controls['email'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls['email'].hasError('email')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_EMAIL' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'email' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- additional fields -->\n @for (definition of additionalFields; track definition.key) {\n <ng-container [matColumnDef]=\"definition.key\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ definition.label | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = definition.key\">\n @if (row.editing) {\n <app-form-field\n floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"row.validator.controls[definition.key]\"\n [required]=\"definition.extra?.users?.required\"\n [autofocus]=\"row.editing && focusColumn === definition.key\"\n ></app-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: definition.key | displayWith: getDisplayValueFn(definition) }}\n </ion-label>\n }\n </td>\n </ng-container>\n }\n\n <!-- profile column -->\n <ng-container matColumnDef=\"profile\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.PROFILE' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'profile'\">\n @if (row.editing) {\n <mat-form-field>\n <mat-select\n [formControl]=\"row.validator | formGetControl: 'mainProfile'\"\n [placeholder]=\"'USER.PROFILE' | translate\"\n >\n @for (item of profiles; track item) {\n <mat-option [value]=\"item\">\n {{ 'USER.PROFILE_ENUM.' + item | uppercase | translate }}\n </mat-option>\n }\n </mat-select>\n @if (row.validator.controls['mainProfile'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let profile = row.validator | formGetValue: 'mainProfile';\n @if (profile) {\n <ion-label appAutoTitle>{{ 'USER.PROFILE_ENUM.' + profile | uppercase | translate }}</ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.STATUS' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'status'\">\n @if (row.editing) {\n @let control = row.validator | formGetControl: 'statusId';\n <mat-form-field>\n <mat-select [formControl]=\"control\" [placeholder]=\"'REFERENTIAL.STATUS' | translate\">\n <mat-select-trigger>\n @if (control.value >= 0) {\n <span>\n {{ statusById[control.value]?.label | translate }}\n </span>\n }\n </mat-select-trigger>\n @for (item of statusList; track item.id) {\n <mat-option [value]=\"item.id\">\n <mat-icon>\n <ion-icon [name]=\"item.icon\"></ion-icon>\n </mat-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n </mat-select>\n @if (control.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n @let statusId = row.validator | formGetValue: 'statusId';\n @if (statusId >= 0) {\n <ion-label appAutoTitle>\n {{ (statusById | mapGet: statusId)?.label | translate }}\n </ion-label>\n }\n }\n </td>\n </ng-container>\n\n <!-- username -->\n <ng-container matColumnDef=\"username\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'username'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.username\"\n [placeholder]=\"'USER.USERNAME' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'username'\"\n />\n @if (row.validator.controls.username.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'username' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- username extranet -->\n <ng-container matColumnDef=\"usernameExtranet\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.USERNAME_EXTRANET' | translate }}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'usernameExtranet'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls.usernameExtranet\"\n [placeholder]=\"'USER.USERNAME_EXTRANET' | translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn === 'usernameExtranet'\"\n />\n @if (row.validator.controls.usernameExtranet.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'usernameExtranet' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- pubkey -->\n <ng-container matColumnDef=\"pubkey\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span>{{ 'USER.PUBKEY' | translate }}</span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n [title]=\"!showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n [matTooltip]=\"showTooltip ? (row.validator.controls.pubkey.valueChanges | async) : ''\"\n (click)=\"focusColumn = 'pubkey'\"\n >\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator.controls['pubkey']\"\n [placeholder]=\"'USER.PUBKEY' | translate\"\n [readonly]=\"!row.editing\"\n autocomplete=\"off\"\n />\n @if (row.validator.controls.pubkey.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if (row.validator.controls.pubkey.hasError('pubkey')) {\n <mat-error>\n <span>{{ 'ERROR.FIELD_NOT_VALID_PUBKEY' | translate }}</span>\n </mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'pubkey' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Creation date column -->\n <ng-container matColumnDef=\"creationDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.CREATION_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.creationDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Update date column -->\n <ng-container matColumnDef=\"updateDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'USER.UPDATE_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text class=\"ion-text-end\" color=\"medium\">\n {{ row.currentData.updateDate | dateFormat: { time: true } }}\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmEditCreateClick)=\"confirmEditCreate($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [canCancel]=\"false\"\n >\n <!-- table options menu -->\n <button\n matHeader\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_OPTIONS' | translate) : ''\"\n [matMenuTriggerFor]=\"tableOptionsMenu\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-error]=\"row.validator.invalid\"\n [class.mat-row-dirty]=\"row.currentData.dirty\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-mdc-row-selected]=\"row.editing || (!canEdit && selection | isSelected: row)\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"!row.validator?.valid\"\n ></tr>\n </table>\n\n <!-- D\u00E9clencheur invisible pour le mat-menu -->\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n </div>\n</ion-content>\n\n<!-- Table options menu -->\n<mat-menu #tableOptionsMenu=\"matMenu\" xPosition=\"after\">\n <!-- display columns -->\n <button mat-menu-item (click)=\"openSelectColumnsModal($event)\">\n <mat-icon>view_column</mat-icon>\n <ion-label>{{ 'COMMON.DISPLAYED_COLUMNS_DOTS' | translate }}</ion-label>\n </button>\n\n @if (usePageSettings) {\n <mat-divider></mat-divider>\n\n <!-- Compact mode -->\n <button mat-menu-item (click)=\"toggleCompactMode()\">\n <mat-icon>{{ compact ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <ion-label>{{ 'COMMON.BTN_COMPACT_ROWS' | translate }}</ion-label>\n </button>\n }\n</mat-menu>\n\n<ion-footer>\n @if (showPaginator) {\n <mat-paginator\n class=\"mat-paginator-footer\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (showFormButtons) {\n <app-form-buttons-bar\n (onCancel)=\"cancel($event)\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) === true || (dirtySubject | async) !== true\"\n >\n <!-- error -->\n @if (error) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow()\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".mat-expansion-panel{margin-bottom:5px}.mat-expansion-panel .form-container mat-form-field{width:100%}.mat-expansion-panel mat-action-row ion-label{line-height:36px}ion-content{height:calc(100% - var(--table-offset, 0px))}.table-container{height:100%}.table-container.has-toolbar{--table-toolbar-height: var(--mat-toolbar-height, 64px)}.table-container.has-paginator{--table-paginator-height: var(--app-paginator-height, 34px)}.table-container.has-form-buttons{--form-buttons-height: var(--ion-toolbar-height, 56px)}.mat-mdc-table .mat-column-avatar{--avatar-width: 40px;--avatar-height: 40px;width:calc(var(--avatar-width) + 10px)}.mat-mdc-table .mat-column-avatar ion-avatar{--border-radius: 5px !important;--border-width: 1px !important;--border-color: rgba(var(--ion-color-secondary-rgb), .5) !important;height:var(--avatar-height);max-height:var(--avatar-height);width:var(--avatar-width);max-width:var(--avatar-width);border:solid var(--border-width) var(--border-color);margin:0 2px}.mat-mdc-table .mat-column-avatar ion-avatar svg{max-height:calc(var(--avatar-height) - 1px);max-width:calc(var(--avatar-width) - 1px)}.mat-mdc-table.compact{--mat-row-height: 33px}.mat-mdc-table.compact .mat-column-avatar{--avatar-width: calc(var(--mat-row-height, 33px) - 4px);--avatar-height: calc(var(--mat-row-height, 33px) - 4px)}.mat-mdc-table .mat-column-id{min-width:50px;max-width:120px;padding-right:var(--mat-cell-horizontal-padding, 4px)}.mat-mdc-table .mat-column-lastName,.mat-mdc-table .mat-column-firstName{min-width:80px}.mat-mdc-table .mat-column-email,.mat-mdc-table .mat-column-pubkey,.mat-mdc-table .mat-column-department{min-width:180px}.mat-mdc-table .mat-column-profile{min-width:110px}.mat-mdc-table .mat-column-status,.mat-mdc-table .mat-column-creationDate,.mat-mdc-table .mat-column-updateDate{min-width:130px}\n"] }]
|
|
44876
45553
|
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1$2.UntypedFormBuilder }, { type: AccountService }, { type: i3$5.ValidatorService }, { type: ConfigService }, { type: undefined, decorators: [{
|
|
44877
45554
|
type: Inject,
|
|
44878
45555
|
args: [APP_PERSON_SERVICE]
|
|
@@ -44935,6 +45612,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
44935
45612
|
}], filterExpansionPanel: [{
|
|
44936
45613
|
type: ViewChild,
|
|
44937
45614
|
args: [MatExpansionPanel, { static: true }]
|
|
45615
|
+
}], filterAutocompleteFields: [{
|
|
45616
|
+
type: ViewChildren,
|
|
45617
|
+
args: ['filterAutocompleteFields']
|
|
44938
45618
|
}] } });
|
|
44939
45619
|
|
|
44940
45620
|
class AppSelectUsersModal {
|
|
@@ -45136,14 +45816,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
45136
45816
|
|
|
45137
45817
|
class MessageModule {
|
|
45138
45818
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
45139
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: MessageModule, declarations: [MessageModal, MessageForm], imports: [CommonModule, SharedModule, CorePipesModule, MatChipsModule, SharedDebugModule], exports: [MessageModal, MessageForm] });
|
|
45140
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageModule, providers: [provideChildTranslateService(), { provide: USER_CONTROLLER, useExisting: UserController }], imports: [CommonModule, SharedModule, CorePipesModule, MatChipsModule, SharedDebugModule] });
|
|
45819
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: MessageModule, declarations: [MessageModal, MessageForm], imports: [CommonModule, SharedModule, CorePipesModule, MatChipsModule, SharedDebugModule, SharedMarkdownModule], exports: [MessageModal, MessageForm] });
|
|
45820
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageModule, providers: [provideChildTranslateService(), { provide: USER_CONTROLLER, useExisting: UserController }], imports: [CommonModule, SharedModule, CorePipesModule, MatChipsModule, SharedDebugModule, SharedMarkdownModule] });
|
|
45141
45821
|
}
|
|
45142
45822
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageModule, decorators: [{
|
|
45143
45823
|
type: NgModule,
|
|
45144
45824
|
args: [{
|
|
45145
45825
|
// Do NOT import CoreModule here (circular dependency: CoreModule > home > FeedModule > MessageModule)
|
|
45146
|
-
imports: [CommonModule, SharedModule, CorePipesModule, MatChipsModule, SharedDebugModule],
|
|
45826
|
+
imports: [CommonModule, SharedModule, CorePipesModule, MatChipsModule, SharedDebugModule, SharedMarkdownModule],
|
|
45147
45827
|
declarations: [MessageModal, MessageForm],
|
|
45148
45828
|
exports: [MessageModal, MessageForm],
|
|
45149
45829
|
providers: [provideChildTranslateService(), { provide: USER_CONTROLLER, useExisting: UserController }],
|
|
@@ -52125,6 +52805,18 @@ function createApolloClientOptions() {
|
|
|
52125
52805
|
const httpLinkInstance = httpLink.create({
|
|
52126
52806
|
uri: (o) => $url.value,
|
|
52127
52807
|
});
|
|
52808
|
+
// Queries link
|
|
52809
|
+
let queryLink = ApolloLink.from([retryLink, authLink, httpLinkInstance]);
|
|
52810
|
+
// Enable @defer (incremental delivery): send queries with @defer using the Apollo fetch link,
|
|
52811
|
+
// because the apollo-angular HttpLink (Angular HttpClient) cannot read a streamed multipart/mixed response
|
|
52812
|
+
const enableDefer = environment.enableGraphqlDefer === true;
|
|
52813
|
+
if (enableDefer) {
|
|
52814
|
+
console.info('[graphql] Enable @defer directive (incremental delivery)');
|
|
52815
|
+
const fetchHttpLink = new HttpLink$1({
|
|
52816
|
+
uri: () => $url.value,
|
|
52817
|
+
});
|
|
52818
|
+
queryLink = ApolloLink.split(isDeferredOperation, ApolloLink.from([retryLink, authLink, httpHeadersToRecordLink, fetchHttpLink]), queryLink);
|
|
52819
|
+
}
|
|
52128
52820
|
// Cache
|
|
52129
52821
|
const cache = new InMemoryCache({
|
|
52130
52822
|
typePolicies,
|
|
@@ -52180,7 +52872,12 @@ function createApolloClientOptions() {
|
|
|
52180
52872
|
// Handle subscriptions
|
|
52181
52873
|
isSubscriptionOperation, wsLink,
|
|
52182
52874
|
// Handle queries
|
|
52183
|
-
|
|
52875
|
+
queryLink)),
|
|
52876
|
+
// @defer support
|
|
52877
|
+
...(enableDefer
|
|
52878
|
+
? { incrementalHandler: new Defer20220824Handler() }
|
|
52879
|
+
: // Remove @defer directives, when disabled
|
|
52880
|
+
{ documentTransform: new DocumentTransform(removeDeferDirectives) }),
|
|
52184
52881
|
// For @apollo/client < 3.11
|
|
52185
52882
|
//connectToDevTools: !environment.production,
|
|
52186
52883
|
// For @apollo/client 3.11+
|
|
@@ -56895,7 +57592,7 @@ class Table2TestPage extends AppAsyncTable {
|
|
|
56895
57592
|
provide: APP_CELL_SELECTION_SERVICE_TOKEN,
|
|
56896
57593
|
useClass: CellSelectionService,
|
|
56897
57594
|
},
|
|
56898
|
-
], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "infiniteScroll", first: true, predicate: IonInfiniteScroll, descendants: true }], usesInheritance: true, ngImport: i0, template: "<app-toolbar\n color=\"primary\"\n [canGoBack]=\"true\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n [backHref]=\"'/testing'\"\n>\n <ion-title>Table 2 (click to select)</ion-title>\n\n <ion-buttons slot=\"end\">\n @if (selection | isEmptySelection) {\n <input matInput type=\"number\" step=\"1\" style=\"color: black; width: 50px\" [(ngModel)]=\"rowHeight\" />\n\n <!-- Add -->\n @if (canEdit && !mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n\n <!-- save -->\n @if (mobile) {\n <button mat-icon-button [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n }\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n @if (canEdit) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n <!-- duplicate -->\n @if (canEdit && selection.selected | isArrayLength: { equals: 1 }) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n @if (mobile) {\n <ion-refresher slot=\"fixed\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n }\n\n <!-- error -->\n @if (mobile && error) {\n <ion-item lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <!-- search -->\n <mat-expansion-panel\n #filterExpansionPanel\n class=\"filter-panel\"\n [class.filter-panel-floating]=\"filterPanelFloating\"\n [class.filter-panel-pinned]=\"!filterPanelFloating\"\n >\n <form class=\"form-container ion-padding-top\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterAndClosePanel($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search text -->\n <mat-form-field>\n <mat-label>{{ 'TABLE.TESTING.SEARCH_TEXT' | translate }}</mat-label>\n <ion-icon matPrefix name=\"search\"></ion-icon>\n <input matInput formControlName=\"searchText\" autocomplete=\"off\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (filterPanelFloating) {\n <button\n mat-icon-button\n color=\"accent\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"!showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\"\n [matTooltip]=\"\n showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '»' : '«' }}</span>\n </mat-icon>\n </button>\n }\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- table -->\n <div [class.table-container]=\"!enableInfiniteScroll\" style=\"position: relative\">\n <table\n #table\n mat-table\n matSort\n matSortDisableClear\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n [trackBy]=\"trackByFn\"\n [style.--mat-row-height]=\"rowHeight + 'px'\"\n appCellSelection\n [appSelectableColumns]=\"selectableColumns\"\n [appCellSelectionDisabled]=\"invalid\"\n (appCellSelectionChange)=\"onCellSelectionChange($event)\"\n (appCellRightClick)=\"onCellRightClick($event)\"\n >\n <ng-container matColumnDef=\"select\" [sticky]=\"checkBoxSelection\" [class.mat-column-sticky]=\"checkBoxSelection\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n @if (selection | isMultipleSelection) {\n <mat-checkbox\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n (change)=\"$event ? masterToggle() : null\"\n ></mat-checkbox>\n }\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n <mat-checkbox [checked]=\"selection | isSelected: row\" (click)=\"toggleSelectRow($event, row)\"></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef>\n <span mat-sort-header>\n <ion-label title=\"Id\">#</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'id' }\">\n @if (!readOnly && row.id === -1 && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n @if ((row.validator | formGetControl: 'id').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'id').hasError('alreadyExists')) {\n <mat-error>{{ 'ERROR.FIELD_NOT_UNIQUE_ID' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Name column -->\n <ng-container matColumnDef=\"name\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label\n [title]=\"!showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n >\n {{ 'TABLE.TESTING.NAME' | translate }}\n </ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"focusColumn = 'name'\"\n [appCellId]=\"{ rowId: row.id, columnName: 'name' }\"\n >\n @if (!readOnly && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n [required]=\"true\"\n [formControl]=\"row.validator | formGetControl: 'name'\"\n [placeholder]=\"'TABLE.TESTING.NAME' | translate\"\n [appAutofocus]=\"row.id === -1 || focusColumn === 'name'\"\n />\n <ng-content select=\"[suffix]\"></ng-content>\n @if ((row.validator | formGetControl: 'name').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('maxlength')) {\n <mat-error>{{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('minlength')) {\n <mat-error>{{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Level column -->\n <ng-container matColumnDef=\"levelId\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <span mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LEVEL_ID' | translate }}</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'levelId' }\">\n @if (!readOnly && row.editing) {\n <mat-autocomplete-field\n panelWidth=\"750px\"\n [formControl]=\"row.validator | formGetControl: 'levelId'\"\n [placeholder]=\"'TABLE.TESTING.LEVEL_ID' | translate\"\n floatLabel=\"never\"\n [required]=\"true\"\n [config]=\"autocompleteFields.level\"\n [highlightAccent]=\"true\"\n >\n @if ((row.validator | formGetControl: 'levelId').hasError('invalid')) {\n <mat-error matError>{{ 'ERROR.FIELD_INVALID' | translate }}</mat-error>\n }\n </mat-autocomplete-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n autocompleteFields.level?.displayWith(\n (row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n ) ||\n ((row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n | referentialToString)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'checkbox'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean') || formBuilder.control(row.currentData['boolean'])\n \"\n [compact]=\"true\"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean2\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 2</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean2' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n [floatLabel]=\"'never'\"\n [style]=\"'radio'\"\n [showRadio]=\"true\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean3\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 3</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n [canCancel]=\"false\"\n [style]=\"'table'\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [cellTemplate]=\"cellInjection\"\n >\n <!-- cell injection-->\n <ng-template #cellInjection let-row>\n @if (row.editing && !row.validator.dirty) {\n <span>-</span>\n }\n </ng-template>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-selected]=\"row.editing\"\n [class.mat-row-error]=\"row.invalid\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-row-dirty]=\"row.dirty\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.invalid\"\n ></tr>\n </table>\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else {\n @if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n }\n\n @if (enableInfiniteScroll) {\n <ion-infinite-scroll [threshold]=\"mobile ? '10%' : '2%'\" position=\"bottom\" (ionInfinite)=\"fetchMore($event)\">\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n }\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n @if (!enableInfiniteScroll) {\n <mat-paginator\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (canEdit && !mobile) {\n <app-form-buttons-bar (onCancel)=\"load()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject | async) || !dirty\">\n <!-- error -->\n @if (errorSubject | async) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (canEdit && mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".table-container .mat-mdc-table .mat-column-select{min-width:60px;width:60px}.table-container .mat-mdc-table .mat-column-id{width:90px;min-width:90px;max-width:90px}.table-container .mat-mdc-table .mat-column-label,.table-container .mat-mdc-table .mat-column-name,.table-container .mat-mdc-table .mat-column-levelId,.table-container .mat-mdc-table .mat-column-statusId{min-width:150px;width:150px}.table-container .mat-mdc-table .mat-column-comments{min-width:100px;max-width:100px;width:100px}.table-container .mat-mdc-table .mat-column-updateDate{min-width:110px;max-width:110px;width:110px}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$2.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$2.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "showPanelHeader", "showPanelFooter", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "reloadItemsOnFocus", "clearInvalidValueOnBlur", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "multiple", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "debug", "showSearchBar", "stickySearchBar", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "splitSearchText", "selectInputContentOnFocus", "selectInputContentOnFocusDelay", "previewImplicitValue", "showFavorites", "toggleFavoriteTitle", "favoriteItems", "colSizes", "class", "filter", "readonly", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keydown.backspace", "keyup.enter", "arrowUp", "arrowDown", "enter", "selectionChange", "openedChange", "toggleFavorite"] }, { kind: "component", type: MatBooleanField, selector: "mat-boolean-field", inputs: ["disabled", "formControl", "formControlName", "placeholder", "floatLabel", "appearance", "subscriptSizing", "readonly", "required", "compact", "autofocus", "style", "buttonsColCount", "class", "yesLabel", "noLabel", "showButtonIcons", "yesIcon", "noIcon", "clearable", "labelPosition", "tabindex", "showRadio", "value"], outputs: ["keyup.enter", "focus", "blur"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTooltipDirective, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: ["appAutoTooltip"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: ActionsColumnComponent, selector: "app-actions-column", inputs: ["matColumnDef", "style", "showPendingSpinner", "stickyEnd", "canCancel", "canConfirm", "canDelete", "canBackward", "canForward", "canConfirmAndAdd", "dirtyIcon", "optionsTitle", "class", "cellTemplateStart", "cellTemplate"], outputs: ["optionsClick", "cancelOrDeleteClick", "confirmEditCreateClick", "confirmAndAddClick", "backward", "forward"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResizableComponent, selector: "th[resizable]", inputs: ["resizable"], outputs: ["sizeChanged"] }, { kind: "directive", type: ResizableDirective, selector: "[resizable]", inputs: ["minWidth"], outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "directive", type: CellSelectionDirective, selector: "[appCellSelection]", inputs: ["appSelectableColumns", "appCellSelectionDisabled"], outputs: ["appCellSelectionChange", "appCellRightClick"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: PropertyGetPipe, name: "propertyGet" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: IsMultipleSelectionPipe, name: "isMultipleSelection" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }, { kind: "pipe", type: ReferentialToStringPipe, name: "referentialToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
57595
|
+
], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "infiniteScroll", first: true, predicate: IonInfiniteScroll, descendants: true }], usesInheritance: true, ngImport: i0, template: "<app-toolbar\n color=\"primary\"\n [canGoBack]=\"true\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n [backHref]=\"'/testing'\"\n>\n <ion-title>Table 2 (click to select)</ion-title>\n\n <ion-buttons slot=\"end\">\n @if (selection | isEmptySelection) {\n <input matInput type=\"number\" step=\"1\" style=\"color: black; width: 50px\" [(ngModel)]=\"rowHeight\" />\n\n <!-- Add -->\n @if (canEdit && !mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n\n <!-- save -->\n @if (mobile) {\n <button mat-icon-button [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n }\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n @if (canEdit) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n <!-- duplicate -->\n @if (canEdit && selection.selected | isArrayLength: { equals: 1 }) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n @if (mobile) {\n <ion-refresher slot=\"fixed\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n }\n\n <!-- error -->\n @if (mobile && error) {\n <ion-item lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <!-- search -->\n <mat-expansion-panel\n #filterExpansionPanel\n class=\"filter-panel\"\n [class.filter-panel-floating]=\"filterPanelFloating\"\n [class.filter-panel-pinned]=\"!filterPanelFloating\"\n >\n <form class=\"form-container ion-padding-top\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterAndClosePanel($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search text -->\n <mat-form-field>\n <mat-label>{{ 'TABLE.TESTING.SEARCH_TEXT' | translate }}</mat-label>\n <ion-icon matPrefix name=\"search\"></ion-icon>\n <input matInput formControlName=\"searchText\" autocomplete=\"off\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (filterPanelFloating) {\n <button\n mat-icon-button\n color=\"accent\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"!showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\"\n [matTooltip]=\"\n showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '»' : '«' }}</span>\n </mat-icon>\n </button>\n }\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- table -->\n <div [class.table-container]=\"!enableInfiniteScroll\" style=\"position: relative\">\n <table\n #table\n mat-table\n matSort\n matSortDisableClear\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n [trackBy]=\"trackByFn\"\n [style.--mat-row-height]=\"rowHeight + 'px'\"\n appCellSelection\n [appSelectableColumns]=\"selectableColumns\"\n [appCellSelectionDisabled]=\"invalid\"\n (appCellSelectionChange)=\"onCellSelectionChange($event)\"\n (appCellRightClick)=\"onCellRightClick($event)\"\n >\n <ng-container matColumnDef=\"select\" [sticky]=\"checkBoxSelection\" [class.mat-column-sticky]=\"checkBoxSelection\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n @if (selection | isMultipleSelection) {\n <mat-checkbox\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n (change)=\"$event ? masterToggle() : null\"\n ></mat-checkbox>\n }\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n <mat-checkbox [checked]=\"selection | isSelected: row\" (click)=\"toggleSelectRow($event, row)\"></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef>\n <span mat-sort-header>\n <ion-label title=\"Id\">#</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'id' }\">\n @if (!readOnly && row.id === -1 && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n @if ((row.validator | formGetControl: 'id').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'id').hasError('alreadyExists')) {\n <mat-error>{{ 'ERROR.FIELD_NOT_UNIQUE_ID' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Name column -->\n <ng-container matColumnDef=\"name\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label\n [title]=\"!showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n >\n {{ 'TABLE.TESTING.NAME' | translate }}\n </ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"focusColumn = 'name'\"\n [appCellId]=\"{ rowId: row.id, columnName: 'name' }\"\n >\n @if (!readOnly && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n [required]=\"true\"\n [formControl]=\"row.validator | formGetControl: 'name'\"\n [placeholder]=\"'TABLE.TESTING.NAME' | translate\"\n [appAutofocus]=\"row.id === -1 || focusColumn === 'name'\"\n />\n <ng-content select=\"[suffix]\"></ng-content>\n @if ((row.validator | formGetControl: 'name').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('maxlength')) {\n <mat-error>{{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('minlength')) {\n <mat-error>{{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Level column -->\n <ng-container matColumnDef=\"levelId\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <span mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LEVEL_ID' | translate }}</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'levelId' }\">\n @if (!readOnly && row.editing) {\n <mat-autocomplete-field\n panelWidth=\"750px\"\n [formControl]=\"row.validator | formGetControl: 'levelId'\"\n [placeholder]=\"'TABLE.TESTING.LEVEL_ID' | translate\"\n floatLabel=\"never\"\n [required]=\"true\"\n [config]=\"autocompleteFields.level\"\n [highlightAccent]=\"true\"\n >\n @if ((row.validator | formGetControl: 'levelId').hasError('invalid')) {\n <mat-error matError>{{ 'ERROR.FIELD_INVALID' | translate }}</mat-error>\n }\n </mat-autocomplete-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n autocompleteFields.level?.displayWith(\n (row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n ) ||\n ((row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n | referentialToString)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'checkbox'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean') || formBuilder.control(row.currentData['boolean'])\n \"\n [compact]=\"true\"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean2\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 2</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean2' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n [floatLabel]=\"'never'\"\n [style]=\"'radio'\"\n [showRadio]=\"true\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean3\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 3</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n [canCancel]=\"false\"\n [style]=\"'table'\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [cellTemplate]=\"cellInjection\"\n >\n <!-- cell injection-->\n <ng-template #cellInjection let-row>\n @if (row.editing && !row.validator.dirty) {\n <span>-</span>\n }\n </ng-template>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-selected]=\"row.editing\"\n [class.mat-row-error]=\"row.invalid\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-row-dirty]=\"row.dirty\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.invalid\"\n ></tr>\n </table>\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else {\n @if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n }\n\n @if (enableInfiniteScroll) {\n <ion-infinite-scroll [threshold]=\"mobile ? '10%' : '2%'\" position=\"bottom\" (ionInfinite)=\"fetchMore($event)\">\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n }\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n @if (!enableInfiniteScroll) {\n <mat-paginator\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (canEdit && !mobile) {\n <app-form-buttons-bar (onCancel)=\"load()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject | async) || !dirty\">\n <!-- error -->\n @if (errorSubject | async) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (canEdit && mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".table-container .mat-mdc-table .mat-column-select{min-width:60px;width:60px}.table-container .mat-mdc-table .mat-column-id{width:90px;min-width:90px;max-width:90px}.table-container .mat-mdc-table .mat-column-label,.table-container .mat-mdc-table .mat-column-name,.table-container .mat-mdc-table .mat-column-levelId,.table-container .mat-mdc-table .mat-column-statusId{min-width:150px;width:150px}.table-container .mat-mdc-table .mat-column-comments{min-width:100px;max-width:100px;width:100px}.table-container .mat-mdc-table .mat-column-updateDate{min-width:110px;max-width:110px;width:110px}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$2.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$2.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "showPanelHeader", "showPanelFooter", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "reloadItemsOnFocus", "clearInvalidValueOnBlur", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "multiple", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "debug", "showSearchBar", "stickySearchBar", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "splitSearchText", "selectInputContentOnFocus", "selectInputContentOnFocusDelay", "previewImplicitValue", "showFavorites", "toggleFavoriteTitle", "favoriteItems", "colSizes", "class", "filter", "readonly", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keydown.backspace", "keyup.enter", "arrowUp", "arrowDown", "enter", "selectionChange", "openedChange", "toggleFavorite"] }, { kind: "component", type: MatBooleanField, selector: "mat-boolean-field", inputs: ["disabled", "formControl", "formControlName", "placeholder", "floatLabel", "appearance", "subscriptSizing", "readonly", "required", "compact", "autofocus", "style", "buttonsColCount", "class", "yesLabel", "noLabel", "showButtonIcons", "yesIcon", "noIcon", "clearable", "labelPosition", "tabindex", "showRadio", "value"], outputs: ["keyup.enter", "focus", "blur"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTooltipDirective, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: ["appAutoTooltip", "appTooltipText"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: ActionsColumnComponent, selector: "app-actions-column", inputs: ["matColumnDef", "style", "showPendingSpinner", "stickyEnd", "canCancel", "canConfirm", "canDelete", "canBackward", "canForward", "canConfirmAndAdd", "dirtyIcon", "optionsTitle", "class", "cellTemplateStart", "cellTemplate"], outputs: ["optionsClick", "cancelOrDeleteClick", "confirmEditCreateClick", "confirmAndAddClick", "backward", "forward"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResizableComponent, selector: "th[resizable]", inputs: ["resizable"], outputs: ["sizeChanged"] }, { kind: "directive", type: ResizableDirective, selector: "[resizable]", inputs: ["minWidth"], outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "directive", type: CellSelectionDirective, selector: "[appCellSelection]", inputs: ["appSelectableColumns", "appCellSelectionDisabled"], outputs: ["appCellSelectionChange", "appCellRightClick"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: PropertyGetPipe, name: "propertyGet" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: IsMultipleSelectionPipe, name: "isMultipleSelection" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }, { kind: "pipe", type: ReferentialToStringPipe, name: "referentialToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
56899
57596
|
}
|
|
56900
57597
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: Table2TestPage, decorators: [{
|
|
56901
57598
|
type: Component,
|
|
@@ -57186,7 +57883,7 @@ class TableTestPage extends AppTable {
|
|
|
57186
57883
|
provide: APP_CELL_SELECTION_SERVICE_TOKEN,
|
|
57187
57884
|
useValue: null, // Disable cell selection if this table is nested (ex: NestedTableTestPage)
|
|
57188
57885
|
},
|
|
57189
|
-
], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "infiniteScroll", first: true, predicate: IonInfiniteScroll, descendants: true }], usesInheritance: true, ngImport: i0, template: "<app-toolbar\n color=\"primary\"\n [canGoBack]=\"true\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n [backHref]=\"'/testing'\"\n>\n <ion-title>Table (click to edit)</ion-title>\n <ion-buttons slot=\"end\">\n @if (selection | isEmptySelection) {\n <input matInput type=\"number\" step=\"1\" style=\"color: black; width: 50px\" [(ngModel)]=\"rowHeight\" />\n\n <!-- Add -->\n @if (canEdit && !mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n\n <!-- save -->\n @if (mobile) {\n <button mat-icon-button [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n }\n\n <!-- start/stop timer to auto-load data -->\n @if (!timer) {\n <ion-button (click)=\"startTimer()\">Start reload</ion-button>\n }\n @if (timer) {\n <ion-button (click)=\"stopTimer()\" color=\"accent\">Stop reload</ion-button>\n }\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n @if (canEdit) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n <!-- duplicate -->\n @if (canEdit && selection.selected | isArrayLength: { equals: 1 }) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n @if (mobile) {\n <ion-refresher slot=\"fixed\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n }\n\n <!-- error -->\n @if (mobile && error) {\n <ion-item lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <!-- search -->\n <mat-expansion-panel\n #filterExpansionPanel\n class=\"filter-panel\"\n [class.filter-panel-floating]=\"filterPanelFloating\"\n [class.filter-panel-pinned]=\"!filterPanelFloating\"\n >\n <form class=\"form-container ion-padding-top\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterAndClosePanel($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search text -->\n <mat-form-field>\n <mat-label>{{ 'TABLE.TESTING.SEARCH_TEXT' | translate }}</mat-label>\n <ion-icon matPrefix name=\"search\"></ion-icon>\n <input matInput formControlName=\"searchText\" autocomplete=\"off\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (filterPanelFloating) {\n <button\n mat-icon-button\n color=\"accent\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"!showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\"\n [matTooltip]=\"\n showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '»' : '«' }}</span>\n </mat-icon>\n </button>\n }\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- table -->\n <div [class.table-container]=\"!enableInfiniteScroll\">\n <table\n #table\n mat-table\n matSort\n matSortDisableClear\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n [trackBy]=\"trackByFn\"\n [style.--mat-row-height]=\"rowHeight + 'px'\"\n >\n <!-- group header cells -->\n <ng-container matColumnDef=\"top-start\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!canEdit\" [attr.colspan]=\"2\">\n <!-- start spacer -->\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"group-1\">\n <th mat-header-cell *matHeaderCellDef [attr.colspan]=\"3\">\n <ion-label>{{ i18nColumnPrefix + 'GROUP_1' | translate }}</ion-label>\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"group-2\">\n <th mat-header-cell *matHeaderCellDef [attr.colspan]=\"displayedColumns.length - 6\">\n <ion-label>{{ i18nColumnPrefix + 'GROUP_2' | translate }}</ion-label>\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"top-end\" [stickyEnd]=\"stickyEnd\">\n <th mat-header-cell *matHeaderCellDef>\n <!-- end spacer -->\n <ion-label></ion-label>\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"select\" [sticky]=\"sticky\" [class.mat-column-sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!canEdit\">\n @if (selection | isMultipleSelection) {\n <mat-checkbox\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n (change)=\"$event ? masterToggle() : null\"\n ></mat-checkbox>\n }\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!canEdit\">\n <mat-checkbox\n (click)=\"toggleSelectRow($event, row)\"\n [checked]=\"selection | isSelected: row\"\n tabindex=\"-1\"\n ></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\" [class.mat-column-sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">{{ row.currentData?.id }}</td>\n </ng-container>\n\n <!-- Label column -->\n <ng-container matColumnDef=\"label\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LABEL' | translate }}</ion-label>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"focusColumn = 'label'\"\n [appCellId]=\"{ rowId: row.id, columnName: 'label' }\"\n >\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n [formControl]=\"row.validator.controls['label']\"\n [placeholder]=\"'TABLE.TESTING.LABEL' | translate\"\n [appAutofocus]=\"focusColumn === 'label'\"\n [readonly]=\"!row.editing\"\n />\n @if (row.validator.controls['label'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ row.validator | formGetValue: 'label' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Name column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.NAME' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'name'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator?.controls.name\"\n [placeholder]=\"'TABLE.TESTING.NAME' | translate\"\n [appAutofocus]=\"focusColumn === 'name'\"\n />\n @if (row.validator?.controls.name.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'name' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Level column -->\n <ng-container matColumnDef=\"levelId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LEVEL_ID' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'levelId'\">\n @if (row.editing) {\n <mat-autocomplete-field\n [formControl]=\"row.validator.controls.levelId\"\n [placeholder]=\"'TABLE.TESTING.LEVEL_ID' | translate\"\n floatLabel=\"never\"\n [config]=\"autocompleteFields.level\"\n [readonly]=\"!row.editing\"\n [autofocus]=\"focusColumn === 'levelId'\"\n [required]=\"true\"\n ></mat-autocomplete-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'levelId' | referentialToString: autocompleteFields.level.attributes }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"statusId\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.STATUS' | translate }}</span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n [class.mat-form-field-disabled]=\"!row.editing\"\n (click)=\"focusColumn = 'statusId'\"\n >\n <mat-form-field>\n <mat-select\n [formControl]=\"row.validator.controls['statusId']\"\n [placeholder]=\"i18nColumnPrefix + 'STATUS_ID' | translate\"\n >\n @for (item of statusList; track item) {\n <mat-option [value]=\"item.id\">\n <mat-icon><ion-icon [name]=\"item.icon\"></ion-icon></mat-icon>\n <mat-label>{{ item.label | translate }}</mat-label>\n </mat-option>\n }\n </mat-select>\n @if (row.validator.controls['statusId'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- Enum column -->\n <!-- NOTE : Never used in table -->\n <!-- <ng-container matColumnDef=\"values\">-->\n <!-- <th mat-header-cell *matHeaderCellDef>-->\n <!-- <span>{{ 'Enums' | translate }}</span>-->\n <!-- </th>-->\n <!-- <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">-->\n <!-- <app-form-field-->\n <!-- [formControl]=\"row.validator|formGetControl:'properties.values'\"-->\n <!-- [definition]=\"columnDefinitions['values']\"-->\n <!-- ></app-form-field>-->\n <!-- </td>-->\n <!-- </ng-container>-->\n\n <!-- boolean (as checkbox) -->\n <ng-container matColumnDef=\"boolean\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"true\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'checkbox'\"\n placeholder=\"Oui/Non\"\n [formControl]=\"row.validator | formGetControl: 'properties.boolean'\"\n [compact]=\"true\"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'properties.boolean') ? '✔' : '✘' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- date -->\n <ng-container matColumnDef=\"date\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"true\" class=\"mat-cell-date-time\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Date</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-cell-date-time\" (click)=\"focusColumn = 'date'\">\n @if (!readOnly && row.editing) {\n <mat-date-time-field\n floatLabel=\"never\"\n [formControl]=\"row.validator | formGetControl: 'properties.date'\"\n [autofocus]=\"focusColumn === 'date'\"\n placeholder=\"Date/Time\"\n [compact]=\"true\"\n ></mat-date-time-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'properties.date' | dateFormat: { time: true } }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- latitude -->\n <ng-container matColumnDef=\"latitude\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"true\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Latitude</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'latitude'\">\n @if (!readOnly && row.editing) {\n <mat-latlong-field\n floatLabel=\"never\"\n [formControl]=\"row.validator | formGetControl: 'properties.latitude'\"\n placeholder=\"Latitude\"\n [latLongPattern]=\"'DDMM'\"\n [type]=\"'latitude'\"\n [autofocus]=\"focusColumn === 'latitude'\"\n ></mat-latlong-field>\n } @else {\n <ion-label appAutoTitle>\n {{\n row.validator\n | formGetValue: 'properties.latitude'\n | latitudeFormat: { pattern: 'DDMM', placeholderChar: '0' }\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Creation date column -->\n <ng-container matColumnDef=\"updateDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.UPDATE_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text color=\"medium\">\n <small>\n {{ row.validator | formGetValue: 'creationDate' | dateFormat: { time: true } }}\n </small>\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Comment column -->\n <ng-container matColumnDef=\"comments\">\n <th mat-header-cell *matHeaderCellDef>\n <ion-label>{{ 'TABLE.TESTING.COMMENTS' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (row.editing) {\n <mat-form-field>\n <!--<textarea matInput [formControl]=\"row.validator?.controls.comments\"\n [placeholder]=\"'TABLE.TESTING.COMMENTS'|translate\"\n [readonly]=\"!row.editing\"></textarea>-->\n <input\n type=\"text\"\n matInput\n [formControl]=\"row.validator?.controls.comments\"\n [placeholder]=\"'TABLE.TESTING.COMMENTS' | translate\"\n [readonly]=\"!row.editing\"\n />\n </mat-form-field>\n } @else {\n <ion-icon\n [color]=\"row.validator?.controls.comments.value ? 'tertiary' : 'medium'\"\n name=\"chatbox\"\n slot=\"icon-only\"\n ></ion-icon>\n }\n </td>\n </ng-container>\n\n <!-- Actions column -->\n <app-nav-actions-column\n [stickyEnd]=\"stickyEnd\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n [cellTemplate]=\"cellInjection\"\n [showPending]=\"true\"\n >\n <!-- cell injection-->\n <ng-template #cellInjection let-row>\n @if (row.editing && !row.validator.dirty) {\n <span>-</span>\n }\n </ng-template>\n </app-nav-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"groupColumns\"></tr>\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-mdc-row-selected]=\"row.editing\"\n [class.mat-mdc-row-error]=\"row.invalid\"\n [class.mat-mdc-row-disabled]=\"!row.editing\"\n [class.mat-mdc-row-dirty]=\"row.dirty\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.invalid\"\n ></tr>\n </table>\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else {\n @if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n }\n\n @if (enableInfiniteScroll) {\n <ion-infinite-scroll [threshold]=\"mobile ? '10%' : '2%'\" position=\"bottom\" (ionInfinite)=\"fetchMore($event)\">\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n }\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n @if (!enableInfiniteScroll) {\n <mat-paginator\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (canEdit && !mobile) {\n <app-form-buttons-bar (onCancel)=\"load()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject | async) || !dirty\">\n <!-- error -->\n @if (errorSubject | async) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (canEdit && mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".table-container .mat-mdc-table{--mat-column-actions-min-width: 62px;--mat-column-actions-max-width: 62px}.table-container .mat-mdc-table .mat-row-selected{padding:3px}.table-container .mat-mdc-table .mat-column-select{min-width:30px}.table-container .mat-mdc-table .mat-column-id{min-width:30px;max-width:30px}.table-container .mat-mdc-table .mat-column-label,.table-container .mat-mdc-table .mat-column-name,.table-container .mat-mdc-table .mat-column-levelId,.table-container .mat-mdc-table .mat-column-statusId{min-width:150px}.table-container .mat-mdc-table .mat-column-comments{min-width:100px;max-width:100px}.table-container .mat-mdc-table .mat-column-updateDate{min-width:110px;max-width:110px}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$2.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$2.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i9$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "showPanelHeader", "showPanelFooter", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "reloadItemsOnFocus", "clearInvalidValueOnBlur", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "multiple", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "debug", "showSearchBar", "stickySearchBar", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "splitSearchText", "selectInputContentOnFocus", "selectInputContentOnFocusDelay", "previewImplicitValue", "showFavorites", "toggleFavoriteTitle", "favoriteItems", "colSizes", "class", "filter", "readonly", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keydown.backspace", "keyup.enter", "arrowUp", "arrowDown", "enter", "selectionChange", "openedChange", "toggleFavorite"] }, { kind: "component", type: MatLatLongField, selector: "mat-latlong-field", inputs: ["formControl", "formControlName", "type", "latLongPattern", "maxDecimals", "required", "floatLabel", "placeholder", "defaultSign", "autofocus", "mobile", "clearable", "class", "tabindex", "appearance", "readonly", "subscriptSizing"] }, { kind: "component", type: MatDateTime, selector: "mat-date-time-field", inputs: ["logPrefix", "placeholder", "floatLabel", "mobile", "compact", "autofocus", "clearable", "startDate", "datePickerFilter", "allowNoTime", "dottedMinutesInGap", "timeHoursOnly", "debug", "class", "style", "hourMinWidth", "hourMaxWidth", "appearance", "subscriptSizing", "formControl", "formControlName", "required", "readonly", "tabindex"], outputs: ["focus", "blur", "keydown.escape", "keyup.enter"] }, { kind: "component", type: MatBooleanField, selector: "mat-boolean-field", inputs: ["disabled", "formControl", "formControlName", "placeholder", "floatLabel", "appearance", "subscriptSizing", "readonly", "required", "compact", "autofocus", "style", "buttonsColCount", "class", "yesLabel", "noLabel", "showButtonIcons", "yesIcon", "noIcon", "clearable", "labelPosition", "tabindex", "showRadio", "value"], outputs: ["keyup.enter", "focus", "blur"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTitleDirective, selector: "ion-label[appAutoTitle], mat-label[appAutoTitle]", inputs: ["appAutoTitle"] }, { kind: "directive", type: AutoTooltipDirective, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: ["appAutoTooltip"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: NavActionsColumnComponent, selector: "app-nav-actions-column", inputs: ["appTable", "matColumnDef", "style", "stickyEnd", "showPending", "dirtyIcon", "optionsTitle", "class", "cellTemplate", "throttleTime"], outputs: ["optionsClick"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResizableComponent, selector: "th[resizable]", inputs: ["resizable"], outputs: ["sizeChanged"] }, { kind: "directive", type: ResizableDirective, selector: "[resizable]", inputs: ["minWidth"], outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: LatitudeFormatPipe, name: "latitudeFormat" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: IsMultipleSelectionPipe, name: "isMultipleSelection" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }, { kind: "pipe", type: ReferentialToStringPipe, name: "referentialToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
57886
|
+
], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "infiniteScroll", first: true, predicate: IonInfiniteScroll, descendants: true }], usesInheritance: true, ngImport: i0, template: "<app-toolbar\n color=\"primary\"\n [canGoBack]=\"true\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n [backHref]=\"'/testing'\"\n>\n <ion-title>Table (click to edit)</ion-title>\n <ion-buttons slot=\"end\">\n @if (selection | isEmptySelection) {\n <input matInput type=\"number\" step=\"1\" style=\"color: black; width: 50px\" [(ngModel)]=\"rowHeight\" />\n\n <!-- Add -->\n @if (canEdit && !mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n\n <!-- save -->\n @if (mobile) {\n <button mat-icon-button [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n }\n\n <!-- start/stop timer to auto-load data -->\n @if (!timer) {\n <ion-button (click)=\"startTimer()\">Start reload</ion-button>\n }\n @if (timer) {\n <ion-button (click)=\"stopTimer()\" color=\"accent\">Stop reload</ion-button>\n }\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n @if (canEdit) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n <!-- duplicate -->\n @if (canEdit && selection.selected | isArrayLength: { equals: 1 }) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n @if (mobile) {\n <ion-refresher slot=\"fixed\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n }\n\n <!-- error -->\n @if (mobile && error) {\n <ion-item lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <!-- search -->\n <mat-expansion-panel\n #filterExpansionPanel\n class=\"filter-panel\"\n [class.filter-panel-floating]=\"filterPanelFloating\"\n [class.filter-panel-pinned]=\"!filterPanelFloating\"\n >\n <form class=\"form-container ion-padding-top\" [formGroup]=\"filterForm\" (ngSubmit)=\"applyFilterAndClosePanel($event)\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search text -->\n <mat-form-field>\n <mat-label>{{ 'TABLE.TESTING.SEARCH_TEXT' | translate }}</mat-label>\n <ion-icon matPrefix name=\"search\"></ion-icon>\n <input matInput formControlName=\"searchText\" autocomplete=\"off\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (filterPanelFloating) {\n <button\n mat-icon-button\n color=\"accent\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"!showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\"\n [matTooltip]=\"\n showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '»' : '«' }}</span>\n </mat-icon>\n </button>\n }\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- table -->\n <div [class.table-container]=\"!enableInfiniteScroll\">\n <table\n #table\n mat-table\n matSort\n matSortDisableClear\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n [trackBy]=\"trackByFn\"\n [style.--mat-row-height]=\"rowHeight + 'px'\"\n >\n <!-- group header cells -->\n <ng-container matColumnDef=\"top-start\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!canEdit\" [attr.colspan]=\"2\">\n <!-- start spacer -->\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"group-1\">\n <th mat-header-cell *matHeaderCellDef [attr.colspan]=\"3\">\n <ion-label>{{ i18nColumnPrefix + 'GROUP_1' | translate }}</ion-label>\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"group-2\">\n <th mat-header-cell *matHeaderCellDef [attr.colspan]=\"displayedColumns.length - 6\">\n <ion-label>{{ i18nColumnPrefix + 'GROUP_2' | translate }}</ion-label>\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"top-end\" [stickyEnd]=\"stickyEnd\">\n <th mat-header-cell *matHeaderCellDef>\n <!-- end spacer -->\n <ion-label></ion-label>\n </th>\n </ng-container>\n\n <ng-container matColumnDef=\"select\" [sticky]=\"sticky\" [class.mat-column-sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!canEdit\">\n @if (selection | isMultipleSelection) {\n <mat-checkbox\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n (change)=\"$event ? masterToggle() : null\"\n ></mat-checkbox>\n }\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!canEdit\">\n <mat-checkbox\n (click)=\"toggleSelectRow($event, row)\"\n [checked]=\"selection | isSelected: row\"\n tabindex=\"-1\"\n ></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\" [class.mat-column-sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">{{ row.currentData?.id }}</td>\n </ng-container>\n\n <!-- Label column -->\n <ng-container matColumnDef=\"label\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LABEL' | translate }}</ion-label>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"focusColumn = 'label'\"\n [appCellId]=\"{ rowId: row.id, columnName: 'label' }\"\n >\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n [formControl]=\"row.validator.controls['label']\"\n [placeholder]=\"'TABLE.TESTING.LABEL' | translate\"\n [appAutofocus]=\"focusColumn === 'label'\"\n [readonly]=\"!row.editing\"\n />\n @if (row.validator.controls['label'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ row.validator | formGetValue: 'label' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Name column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.NAME' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'name'\">\n @if (row.editing) {\n <mat-form-field>\n <input\n matInput\n [formControl]=\"row.validator?.controls.name\"\n [placeholder]=\"'TABLE.TESTING.NAME' | translate\"\n [appAutofocus]=\"focusColumn === 'name'\"\n />\n @if (row.validator?.controls.name.hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'name' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Level column -->\n <ng-container matColumnDef=\"levelId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LEVEL_ID' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'levelId'\">\n @if (row.editing) {\n <mat-autocomplete-field\n [formControl]=\"row.validator.controls.levelId\"\n [placeholder]=\"'TABLE.TESTING.LEVEL_ID' | translate\"\n floatLabel=\"never\"\n [config]=\"autocompleteFields.level\"\n [readonly]=\"!row.editing\"\n [autofocus]=\"focusColumn === 'levelId'\"\n [required]=\"true\"\n ></mat-autocomplete-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'levelId' | referentialToString: autocompleteFields.level.attributes }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"statusId\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{ 'USER.STATUS' | translate }}</span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n [class.mat-form-field-disabled]=\"!row.editing\"\n (click)=\"focusColumn = 'statusId'\"\n >\n <mat-form-field>\n <mat-select\n [formControl]=\"row.validator.controls['statusId']\"\n [placeholder]=\"i18nColumnPrefix + 'STATUS_ID' | translate\"\n >\n @for (item of statusList; track item) {\n <mat-option [value]=\"item.id\">\n <mat-icon><ion-icon [name]=\"item.icon\"></ion-icon></mat-icon>\n <mat-label>{{ item.label | translate }}</mat-label>\n </mat-option>\n }\n </mat-select>\n @if (row.validator.controls['statusId'].hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- Enum column -->\n <!-- NOTE : Never used in table -->\n <!-- <ng-container matColumnDef=\"values\">-->\n <!-- <th mat-header-cell *matHeaderCellDef>-->\n <!-- <span>{{ 'Enums' | translate }}</span>-->\n <!-- </th>-->\n <!-- <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">-->\n <!-- <app-form-field-->\n <!-- [formControl]=\"row.validator|formGetControl:'properties.values'\"-->\n <!-- [definition]=\"columnDefinitions['values']\"-->\n <!-- ></app-form-field>-->\n <!-- </td>-->\n <!-- </ng-container>-->\n\n <!-- boolean (as checkbox) -->\n <ng-container matColumnDef=\"boolean\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"true\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'checkbox'\"\n placeholder=\"Oui/Non\"\n [formControl]=\"row.validator | formGetControl: 'properties.boolean'\"\n [compact]=\"true\"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'properties.boolean') ? '✔' : '✘' }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- date -->\n <ng-container matColumnDef=\"date\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"true\" class=\"mat-cell-date-time\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Date</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-cell-date-time\" (click)=\"focusColumn = 'date'\">\n @if (!readOnly && row.editing) {\n <mat-date-time-field\n floatLabel=\"never\"\n [formControl]=\"row.validator | formGetControl: 'properties.date'\"\n [autofocus]=\"focusColumn === 'date'\"\n placeholder=\"Date/Time\"\n [compact]=\"true\"\n ></mat-date-time-field>\n } @else {\n <ion-label appAutoTitle>\n {{ row.validator | formGetValue: 'properties.date' | dateFormat: { time: true } }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- latitude -->\n <ng-container matColumnDef=\"latitude\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"true\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Latitude</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'latitude'\">\n @if (!readOnly && row.editing) {\n <mat-latlong-field\n floatLabel=\"never\"\n [formControl]=\"row.validator | formGetControl: 'properties.latitude'\"\n placeholder=\"Latitude\"\n [latLongPattern]=\"'DDMM'\"\n [type]=\"'latitude'\"\n [autofocus]=\"focusColumn === 'latitude'\"\n ></mat-latlong-field>\n } @else {\n <ion-label appAutoTitle>\n {{\n row.validator\n | formGetValue: 'properties.latitude'\n | latitudeFormat: { pattern: 'DDMM', placeholderChar: '0' }\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Creation date column -->\n <ng-container matColumnDef=\"updateDate\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.UPDATE_DATE' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\" class=\"mat-form-field-disabled\">\n @if (row.id !== -1) {\n <ion-text color=\"medium\">\n <small>\n {{ row.validator | formGetValue: 'creationDate' | dateFormat: { time: true } }}\n </small>\n </ion-text>\n }\n </td>\n </ng-container>\n\n <!-- Comment column -->\n <ng-container matColumnDef=\"comments\">\n <th mat-header-cell *matHeaderCellDef>\n <ion-label>{{ 'TABLE.TESTING.COMMENTS' | translate }}</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (row.editing) {\n <mat-form-field>\n <!--<textarea matInput [formControl]=\"row.validator?.controls.comments\"\n [placeholder]=\"'TABLE.TESTING.COMMENTS'|translate\"\n [readonly]=\"!row.editing\"></textarea>-->\n <input\n type=\"text\"\n matInput\n [formControl]=\"row.validator?.controls.comments\"\n [placeholder]=\"'TABLE.TESTING.COMMENTS' | translate\"\n [readonly]=\"!row.editing\"\n />\n </mat-form-field>\n } @else {\n <ion-icon\n [color]=\"row.validator?.controls.comments.value ? 'tertiary' : 'medium'\"\n name=\"chatbox\"\n slot=\"icon-only\"\n ></ion-icon>\n }\n </td>\n </ng-container>\n\n <!-- Actions column -->\n <app-nav-actions-column\n [stickyEnd]=\"stickyEnd\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n [cellTemplate]=\"cellInjection\"\n [showPending]=\"true\"\n >\n <!-- cell injection-->\n <ng-template #cellInjection let-row>\n @if (row.editing && !row.validator.dirty) {\n <span>-</span>\n }\n </ng-template>\n </app-nav-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"groupColumns\"></tr>\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-mdc-row-selected]=\"row.editing\"\n [class.mat-mdc-row-error]=\"row.invalid\"\n [class.mat-mdc-row-disabled]=\"!row.editing\"\n [class.mat-mdc-row-dirty]=\"row.dirty\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.invalid\"\n ></tr>\n </table>\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else {\n @if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n }\n\n @if (enableInfiniteScroll) {\n <ion-infinite-scroll [threshold]=\"mobile ? '10%' : '2%'\" position=\"bottom\" (ionInfinite)=\"fetchMore($event)\">\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n }\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n @if (!enableInfiniteScroll) {\n <mat-paginator\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (canEdit && !mobile) {\n <app-form-buttons-bar (onCancel)=\"load()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject | async) || !dirty\">\n <!-- error -->\n @if (errorSubject | async) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n</ion-footer>\n\n@if (canEdit && mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n}\n", styles: [".table-container .mat-mdc-table{--mat-column-actions-min-width: 62px;--mat-column-actions-max-width: 62px}.table-container .mat-mdc-table .mat-row-selected{padding:3px}.table-container .mat-mdc-table .mat-column-select{min-width:30px}.table-container .mat-mdc-table .mat-column-id{min-width:30px;max-width:30px}.table-container .mat-mdc-table .mat-column-label,.table-container .mat-mdc-table .mat-column-name,.table-container .mat-mdc-table .mat-column-levelId,.table-container .mat-mdc-table .mat-column-statusId{min-width:150px}.table-container .mat-mdc-table .mat-column-comments{min-width:100px;max-width:100px}.table-container .mat-mdc-table .mat-column-updateDate{min-width:110px;max-width:110px}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$2.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$2.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i9$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "showPanelHeader", "showPanelFooter", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "reloadItemsOnFocus", "clearInvalidValueOnBlur", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "multiple", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "debug", "showSearchBar", "stickySearchBar", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "splitSearchText", "selectInputContentOnFocus", "selectInputContentOnFocusDelay", "previewImplicitValue", "showFavorites", "toggleFavoriteTitle", "favoriteItems", "colSizes", "class", "filter", "readonly", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keydown.backspace", "keyup.enter", "arrowUp", "arrowDown", "enter", "selectionChange", "openedChange", "toggleFavorite"] }, { kind: "component", type: MatLatLongField, selector: "mat-latlong-field", inputs: ["formControl", "formControlName", "type", "latLongPattern", "maxDecimals", "required", "floatLabel", "placeholder", "defaultSign", "autofocus", "mobile", "clearable", "class", "tabindex", "appearance", "readonly", "subscriptSizing"] }, { kind: "component", type: MatDateTime, selector: "mat-date-time-field", inputs: ["logPrefix", "placeholder", "floatLabel", "mobile", "compact", "autofocus", "clearable", "startDate", "datePickerFilter", "allowNoTime", "dottedMinutesInGap", "timeHoursOnly", "debug", "class", "style", "hourMinWidth", "hourMaxWidth", "appearance", "subscriptSizing", "formControl", "formControlName", "required", "readonly", "tabindex"], outputs: ["focus", "blur", "keydown.escape", "keyup.enter"] }, { kind: "component", type: MatBooleanField, selector: "mat-boolean-field", inputs: ["disabled", "formControl", "formControlName", "placeholder", "floatLabel", "appearance", "subscriptSizing", "readonly", "required", "compact", "autofocus", "style", "buttonsColCount", "class", "yesLabel", "noLabel", "showButtonIcons", "yesIcon", "noIcon", "clearable", "labelPosition", "tabindex", "showRadio", "value"], outputs: ["keyup.enter", "focus", "blur"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTitleDirective, selector: "ion-label[appAutoTitle], mat-label[appAutoTitle]", inputs: ["appAutoTitle"] }, { kind: "directive", type: AutoTooltipDirective, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: ["appAutoTooltip", "appTooltipText"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: NavActionsColumnComponent, selector: "app-nav-actions-column", inputs: ["appTable", "matColumnDef", "style", "stickyEnd", "showPending", "dirtyIcon", "optionsTitle", "class", "cellTemplate", "throttleTime"], outputs: ["optionsClick"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResizableComponent, selector: "th[resizable]", inputs: ["resizable"], outputs: ["sizeChanged"] }, { kind: "directive", type: ResizableDirective, selector: "[resizable]", inputs: ["minWidth"], outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: LatitudeFormatPipe, name: "latitudeFormat" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: IsMultipleSelectionPipe, name: "isMultipleSelection" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }, { kind: "pipe", type: ReferentialToStringPipe, name: "referentialToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
57190
57887
|
}
|
|
57191
57888
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: TableTestPage, decorators: [{
|
|
57192
57889
|
type: Component,
|
|
@@ -57445,7 +58142,7 @@ class NestedTableTestPage extends AppAsyncTable {
|
|
|
57445
58142
|
provide: APP_CELL_SELECTION_SERVICE_TOKEN,
|
|
57446
58143
|
useClass: CellSelectionService,
|
|
57447
58144
|
},
|
|
57448
|
-
], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "infiniteScroll", first: true, predicate: IonInfiniteScroll, descendants: true }], usesInheritance: true, ngImport: i0, template: "<div style=\"display: flex; flex-wrap: nowrap; width: 100%; height: 100%\">\n <div style=\"flex: 0 0 50%; box-sizing: border-box\">\n <app-toolbar\n color=\"primary\"\n [canGoBack]=\"true\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n [backHref]=\"'/testing'\"\n >\n <ion-title>Table with cell selection</ion-title>\n\n <ion-buttons slot=\"end\">\n @if (selection | isEmptySelection) {\n <mat-label>\n {{ showTable2 ? 'With 2nd table with cell selection' : 'With 2nd table without cell selection' }}\n </mat-label>\n <mat-checkbox [(ngModel)]=\"showTable2\"></mat-checkbox>\n\n <input matInput type=\"number\" step=\"1\" style=\"color: black; width: 50px\" [(ngModel)]=\"rowHeight\" />\n\n <!-- Add -->\n @if (canEdit && !mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n\n <!-- save -->\n @if (mobile) {\n <button mat-icon-button [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n }\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n @if (canEdit) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n <!-- duplicate -->\n @if (canEdit && selection.selected | isArrayLength: { equals: 1 }) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n }\n </ion-buttons>\n </app-toolbar>\n <ion-content class=\"ion-no-padding\">\n @if (mobile) {\n <ion-refresher slot=\"fixed\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n }\n\n <!-- error -->\n @if (mobile && error) {\n <ion-item lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <!-- search -->\n <mat-expansion-panel\n #filterExpansionPanel\n class=\"filter-panel\"\n [class.filter-panel-floating]=\"filterPanelFloating\"\n [class.filter-panel-pinned]=\"!filterPanelFloating\"\n >\n <form\n class=\"form-container ion-padding-top\"\n [formGroup]=\"filterForm\"\n (ngSubmit)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search text -->\n <mat-form-field>\n <mat-label>{{ 'TABLE.TESTING.SEARCH_TEXT' | translate }}</mat-label>\n <ion-icon matPrefix name=\"search\"></ion-icon>\n <input matInput formControlName=\"searchText\" autocomplete=\"off\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (filterPanelFloating) {\n <button\n mat-icon-button\n color=\"accent\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"\n !showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n [matTooltip]=\"\n showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '»' : '«' }}</span>\n </mat-icon>\n </button>\n }\n\n <!-- Close panel -->\n <ion-button\n mat-button\n fill=\"clear\"\n color=\"dark\"\n (click)=\"closeFilterPanel()\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- table -->\n <div [class.table-container]=\"!enableInfiniteScroll\" style=\"position: relative\">\n <table\n #table\n mat-table\n matSort\n matSortDisableClear\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n [trackBy]=\"trackByFn\"\n [style.--mat-row-height]=\"rowHeight + 'px'\"\n appCellSelection\n [appSelectableColumns]=\"selectableColumns\"\n [appCellSelectionDisabled]=\"invalid\"\n (appCellSelectionChange)=\"onCellSelectionChange($event)\"\n (appCellRightClick)=\"onCellRightClick($event)\"\n >\n <ng-container\n matColumnDef=\"select\"\n [sticky]=\"checkBoxSelection\"\n [class.mat-column-sticky]=\"checkBoxSelection\"\n >\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n @if (selection | isMultipleSelection) {\n <mat-checkbox\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n (change)=\"$event ? masterToggle() : null\"\n ></mat-checkbox>\n }\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n <mat-checkbox\n [checked]=\"selection | isSelected: row\"\n (click)=\"toggleSelectRow($event, row)\"\n ></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef>\n <span mat-sort-header>\n <ion-label title=\"Id\">#</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'id' }\">\n @if (!readOnly && row.id === -1 && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n @if ((row.validator | formGetControl: 'id').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'id').hasError('alreadyExists')) {\n <mat-error>{{ 'ERROR.FIELD_NOT_UNIQUE_ID' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Name column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label\n [title]=\"!showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n >\n {{ 'TABLE.TESTING.NAME' | translate }}\n </ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"focusColumn = 'name'\"\n [appCellId]=\"{ rowId: row.id, columnName: 'name' }\"\n >\n @if (!readOnly && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n [required]=\"true\"\n [formControl]=\"row.validator | formGetControl: 'name'\"\n [placeholder]=\"'TABLE.TESTING.NAME' | translate\"\n [appAutofocus]=\"row.id === -1 || focusColumn === 'name'\"\n />\n <ng-content select=\"[suffix]\"></ng-content>\n @if ((row.validator | formGetControl: 'name').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('maxlength')) {\n <mat-error>{{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('minlength')) {\n <mat-error>{{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Level column -->\n <ng-container matColumnDef=\"levelId\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <span mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LEVEL_ID' | translate }}</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'levelId' }\">\n @if (!readOnly && row.editing) {\n <mat-autocomplete-field\n panelWidth=\"750px\"\n [formControl]=\"row.validator | formGetControl: 'levelId'\"\n [placeholder]=\"'TABLE.TESTING.LEVEL_ID' | translate\"\n floatLabel=\"never\"\n [required]=\"true\"\n [config]=\"autocompleteFields.level\"\n [highlightAccent]=\"true\"\n >\n @if ((row.validator | formGetControl: 'levelId').hasError('invalid')) {\n <mat-error matError>{{ 'ERROR.FIELD_INVALID' | translate }}</mat-error>\n }\n </mat-autocomplete-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n autocompleteFields.level?.displayWith(\n (row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n ) ||\n ((row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n | referentialToString)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'checkbox'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean') || formBuilder.control(row.currentData['boolean'])\n \"\n [compact]=\"true\"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean2\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 2</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean2' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n [floatLabel]=\"'never'\"\n [style]=\"'radio'\"\n [showRadio]=\"true\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean3\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 3</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n [canCancel]=\"false\"\n [style]=\"'table'\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [cellTemplate]=\"cellInjection\"\n >\n <!-- cell injection-->\n <ng-template #cellInjection let-row>\n @if (row.editing && !row.validator.dirty) {\n <span>-</span>\n }\n </ng-template>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-selected]=\"row.editing\"\n [class.mat-row-error]=\"row.invalid\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-row-dirty]=\"row.dirty\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.invalid\"\n ></tr>\n </table>\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else {\n @if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n }\n\n @if (enableInfiniteScroll) {\n <ion-infinite-scroll [threshold]=\"mobile ? '10%' : '2%'\" position=\"bottom\" (ionInfinite)=\"fetchMore($event)\">\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n }\n </div>\n </ion-content>\n <ion-footer>\n <!-- Paginator -->\n @if (!enableInfiniteScroll) {\n <mat-paginator\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (canEdit && !mobile) {\n <app-form-buttons-bar (onCancel)=\"load()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject | async) || !dirty\">\n <!-- error -->\n @if (errorSubject | async) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n </ion-footer>\n @if (canEdit && mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n }\n </div>\n <div style=\"flex: 0 0 50%; box-sizing: border-box\">\n @if (showTable2) {\n <app-table2-testing></app-table2-testing>\n } @else {\n <app-table-testing></app-table-testing>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$2.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$2.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "showPanelHeader", "showPanelFooter", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "reloadItemsOnFocus", "clearInvalidValueOnBlur", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "multiple", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "debug", "showSearchBar", "stickySearchBar", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "splitSearchText", "selectInputContentOnFocus", "selectInputContentOnFocusDelay", "previewImplicitValue", "showFavorites", "toggleFavoriteTitle", "favoriteItems", "colSizes", "class", "filter", "readonly", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keydown.backspace", "keyup.enter", "arrowUp", "arrowDown", "enter", "selectionChange", "openedChange", "toggleFavorite"] }, { kind: "component", type: MatBooleanField, selector: "mat-boolean-field", inputs: ["disabled", "formControl", "formControlName", "placeholder", "floatLabel", "appearance", "subscriptSizing", "readonly", "required", "compact", "autofocus", "style", "buttonsColCount", "class", "yesLabel", "noLabel", "showButtonIcons", "yesIcon", "noIcon", "clearable", "labelPosition", "tabindex", "showRadio", "value"], outputs: ["keyup.enter", "focus", "blur"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTooltipDirective, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: ["appAutoTooltip"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: ActionsColumnComponent, selector: "app-actions-column", inputs: ["matColumnDef", "style", "showPendingSpinner", "stickyEnd", "canCancel", "canConfirm", "canDelete", "canBackward", "canForward", "canConfirmAndAdd", "dirtyIcon", "optionsTitle", "class", "cellTemplateStart", "cellTemplate"], outputs: ["optionsClick", "cancelOrDeleteClick", "confirmEditCreateClick", "confirmAndAddClick", "backward", "forward"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResizableComponent, selector: "th[resizable]", inputs: ["resizable"], outputs: ["sizeChanged"] }, { kind: "directive", type: ResizableDirective, selector: "[resizable]", inputs: ["minWidth"], outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "directive", type: CellSelectionDirective, selector: "[appCellSelection]", inputs: ["appSelectableColumns", "appCellSelectionDisabled"], outputs: ["appCellSelectionChange", "appCellRightClick"] }, { kind: "component", type: TableTestPage, selector: "app-table-testing", inputs: ["filterPanelFloating", "enableInfiniteScroll", "sticky", "stickyEnd"] }, { kind: "component", type: Table2TestPage, selector: "app-table2-testing", inputs: ["filterPanelFloating", "sticky", "stickyEnd"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: PropertyGetPipe, name: "propertyGet" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: IsMultipleSelectionPipe, name: "isMultipleSelection" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }, { kind: "pipe", type: ReferentialToStringPipe, name: "referentialToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
58145
|
+
], viewQueries: [{ propertyName: "filterExpansionPanel", first: true, predicate: MatExpansionPanel, descendants: true, static: true }, { propertyName: "infiniteScroll", first: true, predicate: IonInfiniteScroll, descendants: true }], usesInheritance: true, ngImport: i0, template: "<div style=\"display: flex; flex-wrap: nowrap; width: 100%; height: 100%\">\n <div style=\"flex: 0 0 50%; box-sizing: border-box\">\n <app-toolbar\n color=\"primary\"\n [canGoBack]=\"true\"\n [hasValidate]=\"(loadingSubject | async) !== true && (dirtySubject | async) === true\"\n (onValidate)=\"save()\"\n [backHref]=\"'/testing'\"\n >\n <ion-title>Table with cell selection</ion-title>\n\n <ion-buttons slot=\"end\">\n @if (selection | isEmptySelection) {\n <mat-label>\n {{ showTable2 ? 'With 2nd table with cell selection' : 'With 2nd table without cell selection' }}\n </mat-label>\n <mat-checkbox [(ngModel)]=\"showTable2\"></mat-checkbox>\n\n <input matInput type=\"number\" step=\"1\" style=\"color: black; width: 50px\" [(ngModel)]=\"rowHeight\" />\n\n <!-- Add -->\n @if (canEdit && !mobile) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_ADD' | translate) : ''\"\n (click)=\"addRow()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n\n <!-- reset filter -->\n @if (filterCriteriaCount) {\n <button mat-icon-button (click)=\"resetFilter()\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold\">close</mat-icon>\n </button>\n }\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n @if (filterCriteriaCount) {\n <mat-icon\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n aria-hidden=\"false\"\n >\n filter_list_alt\n </mat-icon>\n } @else {\n <mat-icon>filter_list_alt</mat-icon>\n }\n </button>\n\n <!-- save -->\n @if (mobile) {\n <button mat-icon-button [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n }\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n @if (canEdit) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n\n <!-- duplicate -->\n @if (canEdit && selection.selected | isArrayLength: { equals: 1 }) {\n <button\n mat-icon-button\n [title]=\"!showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DUPLICATE' | translate) : ''\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n }\n </ion-buttons>\n </app-toolbar>\n <ion-content class=\"ion-no-padding\">\n @if (mobile) {\n <ion-refresher slot=\"fixed\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n }\n\n <!-- error -->\n @if (mobile && error) {\n <ion-item lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n\n <!-- search -->\n <mat-expansion-panel\n #filterExpansionPanel\n class=\"filter-panel\"\n [class.filter-panel-floating]=\"filterPanelFloating\"\n [class.filter-panel-pinned]=\"!filterPanelFloating\"\n >\n <form\n class=\"form-container ion-padding-top\"\n [formGroup]=\"filterForm\"\n (ngSubmit)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search text -->\n <mat-form-field>\n <mat-label>{{ 'TABLE.TESTING.SEARCH_TEXT' | translate }}</mat-label>\n <ion-icon matPrefix name=\"search\"></ion-icon>\n <input matInput formControlName=\"searchText\" autocomplete=\"off\" />\n <button\n mat-icon-button\n matSuffix\n tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label\n [hidden]=\"(loadingSubject | async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\"\n >\n {{\n (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT')\n | translate\n : {\n count: (totalRowCount | numberFormat),\n }\n }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (filterPanelFloating) {\n <button\n mat-icon-button\n color=\"accent\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"\n !showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n [matTooltip]=\"\n showTooltip ? ((filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate) : ''\n \"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '»' : '«' }}</span>\n </mat-icon>\n </button>\n }\n\n <!-- Close panel -->\n <ion-button\n mat-button\n fill=\"clear\"\n color=\"dark\"\n (click)=\"closeFilterPanel()\"\n [disabled]=\"loadingSubject | async\"\n >\n <ion-text>{{ 'COMMON.BTN_CLOSE' | translate }}</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button\n mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n >\n <ion-text>{{ 'COMMON.BTN_APPLY' | translate }}</ion-text>\n </ion-button>\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- table -->\n <div [class.table-container]=\"!enableInfiniteScroll\" style=\"position: relative\">\n <table\n #table\n mat-table\n matSort\n matSortDisableClear\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n [trackBy]=\"trackByFn\"\n [style.--mat-row-height]=\"rowHeight + 'px'\"\n appCellSelection\n [appSelectableColumns]=\"selectableColumns\"\n [appCellSelectionDisabled]=\"invalid\"\n (appCellSelectionChange)=\"onCellSelectionChange($event)\"\n (appCellRightClick)=\"onCellRightClick($event)\"\n >\n <ng-container\n matColumnDef=\"select\"\n [sticky]=\"checkBoxSelection\"\n [class.mat-column-sticky]=\"checkBoxSelection\"\n >\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n @if (selection | isMultipleSelection) {\n <mat-checkbox\n [checked]=\"this | isAllSelected\"\n [indeterminate]=\"this | isNotAllSelected\"\n (change)=\"$event ? masterToggle() : null\"\n ></mat-checkbox>\n }\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!checkBoxSelection\">\n <mat-checkbox\n [checked]=\"selection | isSelected: row\"\n (click)=\"toggleSelectRow($event, row)\"\n ></mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"sticky\">\n <th mat-header-cell *matHeaderCellDef>\n <span mat-sort-header>\n <ion-label title=\"Id\">#</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'id' }\">\n @if (!readOnly && row.id === -1 && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n @if ((row.validator | formGetControl: 'id').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'id').hasError('alreadyExists')) {\n <mat-error>{{ 'ERROR.FIELD_NOT_UNIQUE_ID' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Name column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label\n [title]=\"!showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('TABLE.TESTING.NAME' | translate) : ''\"\n >\n {{ 'TABLE.TESTING.NAME' | translate }}\n </ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n (click)=\"focusColumn = 'name'\"\n [appCellId]=\"{ rowId: row.id, columnName: 'name' }\"\n >\n @if (!readOnly && row.editing) {\n <mat-form-field>\n <input\n matInput\n autocomplete=\"off\"\n [required]=\"true\"\n [formControl]=\"row.validator | formGetControl: 'name'\"\n [placeholder]=\"'TABLE.TESTING.NAME' | translate\"\n [appAutofocus]=\"row.id === -1 || focusColumn === 'name'\"\n />\n <ng-content select=\"[suffix]\"></ng-content>\n @if ((row.validator | formGetControl: 'name').hasError('required')) {\n <mat-error>{{ 'ERROR.FIELD_REQUIRED' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('maxlength')) {\n <mat-error>{{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}</mat-error>\n }\n @if ((row.validator | formGetControl: 'name').hasError('minlength')) {\n <mat-error>{{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}</mat-error>\n }\n </mat-form-field>\n } @else {\n <ion-label appAutoTooltip>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Level column -->\n <ng-container matColumnDef=\"levelId\">\n <th mat-header-cell *matHeaderCellDef [resizable]=\"resizable\">\n <span mat-sort-header>\n <ion-label>{{ 'TABLE.TESTING.LEVEL_ID' | translate }}</ion-label>\n <ion-text color=\"danger\" [innerHTML]=\"' *'\"></ion-text>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'levelId' }\">\n @if (!readOnly && row.editing) {\n <mat-autocomplete-field\n panelWidth=\"750px\"\n [formControl]=\"row.validator | formGetControl: 'levelId'\"\n [placeholder]=\"'TABLE.TESTING.LEVEL_ID' | translate\"\n floatLabel=\"never\"\n [required]=\"true\"\n [config]=\"autocompleteFields.level\"\n [highlightAccent]=\"true\"\n >\n @if ((row.validator | formGetControl: 'levelId').hasError('invalid')) {\n <mat-error matError>{{ 'ERROR.FIELD_INVALID' | translate }}</mat-error>\n }\n </mat-autocomplete-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n autocompleteFields.level?.displayWith(\n (row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n ) ||\n ((row.validator | formGetValue: 'levelId') || (row.currentData | propertyGet: 'levelId')\n | referentialToString)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'checkbox'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean') || formBuilder.control(row.currentData['boolean'])\n \"\n [compact]=\"true\"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean2\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 2</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'boolean2' }\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n [floatLabel]=\"'never'\"\n [style]=\"'radio'\"\n [showRadio]=\"true\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"boolean3\">\n <th mat-header-cell *matHeaderCellDef cdkDrag [resizable]=\"resizable\">\n <!-- if sortable, wrap the header with a mat-sort-header -->\n <span mat-sort-header>\n <ion-label>Boolean 3</ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n @if (!readOnly && row.editing) {\n <mat-boolean-field\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n } @else {\n <ion-label appAutoTooltip>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n }\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column\n [stickyEnd]=\"stickyEnd\"\n [canCancel]=\"false\"\n [style]=\"'table'\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [cellTemplate]=\"cellInjection\"\n >\n <!-- cell injection-->\n <ng-template #cellInjection let-row>\n @if (row.editing && !row.validator.dirty) {\n <span>-</span>\n }\n </ng-template>\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [class.mat-row-selected]=\"row.editing\"\n [class.mat-row-error]=\"row.invalid\"\n [class.mat-row-disabled]=\"!row.editing\"\n [class.mat-row-dirty]=\"row.dirty\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.invalid\"\n ></tr>\n </table>\n\n @if (loadingSubject | async) {\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n } @else {\n @if (totalRowCount === 0) {\n <ion-item>\n <ion-text color=\"danger\" class=\"text-italic\">{{ 'COMMON.NO_RESULT' | translate }}</ion-text>\n </ion-item>\n }\n }\n\n @if (enableInfiniteScroll) {\n <ion-infinite-scroll [threshold]=\"mobile ? '10%' : '2%'\" position=\"bottom\" (ionInfinite)=\"fetchMore($event)\">\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n }\n </div>\n </ion-content>\n <ion-footer>\n <!-- Paginator -->\n @if (!enableInfiniteScroll) {\n <mat-paginator\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n }\n\n @if (canEdit && !mobile) {\n <app-form-buttons-bar (onCancel)=\"load()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject | async) || !dirty\">\n <!-- error -->\n @if (errorSubject | async) {\n <ion-item lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\"><span [innerHTML]=\"error | translate\"></span></ion-label>\n </ion-item>\n }\n </app-form-buttons-bar>\n }\n </ion-footer>\n @if (canEdit && mobile) {\n <ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n </ion-fab>\n }\n </div>\n <div style=\"flex: 0 0 50%; box-sizing: border-box\">\n @if (showTable2) {\n <app-table2-testing></app-table2-testing>\n } @else {\n <app-table-testing></app-table-testing>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$2.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$2.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$2.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$2.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: i5$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i5$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i5$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i5$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i5$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i5$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i5$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i5$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i5$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i5$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i6$5.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i6$5.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i11$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i8.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$4.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "showPanelHeader", "showPanelFooter", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "reloadItemsOnFocus", "clearInvalidValueOnBlur", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "multiple", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "debug", "showSearchBar", "stickySearchBar", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "splitSearchText", "selectInputContentOnFocus", "selectInputContentOnFocusDelay", "previewImplicitValue", "showFavorites", "toggleFavoriteTitle", "favoriteItems", "colSizes", "class", "filter", "readonly", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keydown.backspace", "keyup.enter", "arrowUp", "arrowDown", "enter", "selectionChange", "openedChange", "toggleFavorite"] }, { kind: "component", type: MatBooleanField, selector: "mat-boolean-field", inputs: ["disabled", "formControl", "formControlName", "placeholder", "floatLabel", "appearance", "subscriptSizing", "readonly", "required", "compact", "autofocus", "style", "buttonsColCount", "class", "yesLabel", "noLabel", "showButtonIcons", "yesIcon", "noIcon", "clearable", "labelPosition", "tabindex", "showRadio", "value"], outputs: ["keyup.enter", "focus", "blur"] }, { kind: "directive", type: AutofocusDirective, selector: "[autofocus], input[appAutofocus]", inputs: ["appAutofocus", "autofocusDelay"] }, { kind: "directive", type: AutoTooltipDirective, selector: "ion-label[appAutoTooltip], mat-label[appAutoTooltip]", inputs: ["appAutoTooltip", "appTooltipText"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "subTitle", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearchInput", "onSearch"] }, { kind: "component", type: FormButtonsBarComponent, selector: "app-form-buttons-bar", inputs: ["disabled", "disabledCancel", "disabledEscape", "classList", "saveButtonColor", "backText", "cancelText", "nextText", "showBack", "showCancel", "showNext", "showSave"], outputs: ["onCancel", "onSave", "onNext", "onBack", "onSaveAndClose", "onSaveAndNext"] }, { kind: "component", type: ActionsColumnComponent, selector: "app-actions-column", inputs: ["matColumnDef", "style", "showPendingSpinner", "stickyEnd", "canCancel", "canConfirm", "canDelete", "canBackward", "canForward", "canConfirmAndAdd", "dirtyIcon", "optionsTitle", "class", "cellTemplateStart", "cellTemplate"], outputs: ["optionsClick", "cancelOrDeleteClick", "confirmEditCreateClick", "confirmAndAddClick", "backward", "forward"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResizableComponent, selector: "th[resizable]", inputs: ["resizable"], outputs: ["sizeChanged"] }, { kind: "directive", type: ResizableDirective, selector: "[resizable]", inputs: ["minWidth"], outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "directive", type: CellSelectionDirective, selector: "[appCellSelection]", inputs: ["appSelectableColumns", "appCellSelectionDisabled"], outputs: ["appCellSelectionChange", "appCellRightClick"] }, { kind: "component", type: TableTestPage, selector: "app-table-testing", inputs: ["filterPanelFloating", "enableInfiniteScroll", "sticky", "stickyEnd"] }, { kind: "component", type: Table2TestPage, selector: "app-table2-testing", inputs: ["filterPanelFloating", "sticky", "stickyEnd"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: PropertyGetPipe, name: "propertyGet" }, { kind: "pipe", type: NumberFormatPipe, name: "numberFormat" }, { kind: "pipe", type: ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }, { kind: "pipe", type: IsEmptySelectionPipe, name: "isEmptySelection" }, { kind: "pipe", type: IsMultipleSelectionPipe, name: "isMultipleSelection" }, { kind: "pipe", type: IsAllSelectedPipe, name: "isAllSelected" }, { kind: "pipe", type: IsNotAllSelectedPipe, name: "isNotAllSelected" }, { kind: "pipe", type: ReferentialToStringPipe, name: "referentialToString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
57449
58146
|
}
|
|
57450
58147
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NestedTableTestPage, decorators: [{
|
|
57451
58148
|
type: Component,
|
|
@@ -58762,12 +59459,117 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
58762
59459
|
}]
|
|
58763
59460
|
}] });
|
|
58764
59461
|
|
|
59462
|
+
// Free license (GPL version 3)
|
|
59463
|
+
// Fake recipients, used by the autocomplete field (no pod needed)
|
|
59464
|
+
const FAKE_PERSONS = [
|
|
59465
|
+
{ id: 1, firstName: 'Demo', lastName: 'Admin', department: { id: 1, label: 'FROM-NORD', name: 'FROM Nord' } },
|
|
59466
|
+
{ id: 2, firstName: 'Demo', lastName: 'Observer', department: { id: 1, label: 'FROM-NORD', name: 'FROM Nord' } },
|
|
59467
|
+
{ id: 3, firstName: 'Charles', lastName: 'Dupond', department: { id: 2, label: 'IFREMER', name: 'Ifremer' } },
|
|
59468
|
+
].map(Person.fromObject);
|
|
59469
|
+
const SAMPLE_BODY = `# Title
|
|
59470
|
+
|
|
59471
|
+
Some **bold**, <u>underlined</u>, ~~strikethrough~~ text, and a ==highlighted== word.
|
|
59472
|
+
|
|
59473
|
+
## Sub title
|
|
59474
|
+
|
|
59475
|
+
- see [the docs](https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components)
|
|
59476
|
+
`;
|
|
59477
|
+
class MessageFormTestingPage {
|
|
59478
|
+
modalCtrl;
|
|
59479
|
+
cd;
|
|
59480
|
+
form;
|
|
59481
|
+
debug = false;
|
|
59482
|
+
canSelectType = true;
|
|
59483
|
+
canAttach = true;
|
|
59484
|
+
bodyPreviewSideBySide = false;
|
|
59485
|
+
modalResult;
|
|
59486
|
+
suggestFn = async (value) => {
|
|
59487
|
+
const search = typeof value === 'string' ? value.toLowerCase() : '';
|
|
59488
|
+
return FAKE_PERSONS.filter((p) => !search || `${p.lastName} ${p.firstName}`.toLowerCase().includes(search));
|
|
59489
|
+
};
|
|
59490
|
+
/** Simulate an upload (with progression) */
|
|
59491
|
+
uploadFn = (file) => {
|
|
59492
|
+
const steps = 5;
|
|
59493
|
+
return timer(0, 100).pipe(take(steps + 1), map((i) => i < steps
|
|
59494
|
+
? { type: HttpEventType.UploadProgress, total: file.size, loaded: (file.size * i) / steps }
|
|
59495
|
+
: new HttpResponse({ body: { finalName: file.name } })));
|
|
59496
|
+
};
|
|
59497
|
+
deleteFn = async (_file) => {
|
|
59498
|
+
await sleep(500);
|
|
59499
|
+
return true;
|
|
59500
|
+
};
|
|
59501
|
+
constructor(modalCtrl, cd) {
|
|
59502
|
+
this.modalCtrl = modalCtrl;
|
|
59503
|
+
this.cd = cd;
|
|
59504
|
+
}
|
|
59505
|
+
ngAfterViewInit() {
|
|
59506
|
+
setTimeout(() => this.reset());
|
|
59507
|
+
}
|
|
59508
|
+
async reset(withSample) {
|
|
59509
|
+
this.form.markAsReady({ emitEvent: false });
|
|
59510
|
+
await this.form.setValue(Message.fromObject({
|
|
59511
|
+
type: MessageTypes.INBOX_MESSAGE,
|
|
59512
|
+
recipients: [FAKE_PERSONS[0]],
|
|
59513
|
+
subject: withSample ? 'Markdown sample' : null,
|
|
59514
|
+
body: withSample ? SAMPLE_BODY : null,
|
|
59515
|
+
}));
|
|
59516
|
+
this.form.markAsLoaded();
|
|
59517
|
+
this.form.enable();
|
|
59518
|
+
this.cd.markForCheck();
|
|
59519
|
+
}
|
|
59520
|
+
async openModal() {
|
|
59521
|
+
const modal = await this.modalCtrl.create({
|
|
59522
|
+
component: MessageModal,
|
|
59523
|
+
componentProps: {
|
|
59524
|
+
suggestFn: this.suggestFn,
|
|
59525
|
+
canSelectType: this.canSelectType,
|
|
59526
|
+
canAttach: this.canAttach,
|
|
59527
|
+
uploadFn: this.uploadFn,
|
|
59528
|
+
deleteFn: this.deleteFn,
|
|
59529
|
+
debug: this.debug,
|
|
59530
|
+
data: Message.fromObject({ recipients: [FAKE_PERSONS[0]] }),
|
|
59531
|
+
},
|
|
59532
|
+
keyboardClose: true,
|
|
59533
|
+
});
|
|
59534
|
+
await modal.present();
|
|
59535
|
+
const { data } = await modal.onDidDismiss();
|
|
59536
|
+
console.info('[message-form-testing] Modal result:', data);
|
|
59537
|
+
this.modalResult = data;
|
|
59538
|
+
this.cd.markForCheck();
|
|
59539
|
+
}
|
|
59540
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageFormTestingPage, deps: [{ token: i2$2.ModalController }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
59541
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MessageFormTestingPage, isStandalone: false, selector: "app-message-form-testing", viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, static: true }], ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n <ion-title>Message form test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <ion-grid class=\"form-container ion-padding-top\">\n <!-- options -->\n <ion-row>\n <ion-col>\n <mat-checkbox [(ngModel)]=\"canSelectType\">canSelectType</mat-checkbox>\n <mat-checkbox [(ngModel)]=\"canAttach\">canAttach</mat-checkbox>\n <mat-checkbox [(ngModel)]=\"bodyPreviewSideBySide\">bodyPreviewSideBySide</mat-checkbox>\n <mat-checkbox [(ngModel)]=\"debug\">debug</mat-checkbox>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"outline\" (click)=\"reset()\">Reset</ion-button>\n <ion-button fill=\"outline\" (click)=\"reset(true)\">Markdown sample</ion-button>\n <ion-button (click)=\"openModal()\">Open in a modal</ion-button>\n </ion-col>\n </ion-row>\n\n <!-- inline form -->\n <ion-row>\n <ion-col size=\"12\" size-lg=\"6\">\n <ion-card>\n <ion-card-content>\n <app-message-form\n #form\n [suggestFn]=\"suggestFn\"\n [canSelectType]=\"canSelectType\"\n [canAttach]=\"canAttach\"\n [bodyPreviewSideBySide]=\"bodyPreviewSideBySide\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [debug]=\"debug\"\n ></app-message-form>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- form value -->\n <ion-col size=\"12\" size-lg=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-subtitle>Form value (valid: {{ form.valid }})</ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <pre>{{ form.form?.value | json }}</pre>\n </ion-card-content>\n </ion-card>\n\n @if (modalResult) {\n <ion-card>\n <ion-card-header>\n <ion-card-subtitle>Modal result</ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <pre>{{ modalResult | json }}</pre>\n </ion-card-content>\n </ion-card>\n }\n </ion-col>\n </ion-row>\n </ion-grid>\n</ion-content>\n", dependencies: [{ kind: "component", type: i2$2.IonBackButton, selector: "ion-back-button" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$2.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$2.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonRow, selector: "ion-row" }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MessageForm, selector: "app-message-form", inputs: ["mobile", "suggestFn", "subjectMinLength", "subjectMaxLength", "bodyMaxLength", "bodyAutoHeight", "canSelectType", "canRecipientFilter", "recipientFilterCount", "canSearchRecipients", "canAttach", "uploadFn", "deleteFn", "maxAttachmentSizeMb", "bodyPreviewSideBySide"], outputs: ["searchRecipients", "attach", "bodyPreviewChange"] }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
59542
|
+
}
|
|
59543
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageFormTestingPage, decorators: [{
|
|
59544
|
+
type: Component,
|
|
59545
|
+
args: [{ selector: 'app-message-form-testing', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n <ion-title>Message form test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <ion-grid class=\"form-container ion-padding-top\">\n <!-- options -->\n <ion-row>\n <ion-col>\n <mat-checkbox [(ngModel)]=\"canSelectType\">canSelectType</mat-checkbox>\n <mat-checkbox [(ngModel)]=\"canAttach\">canAttach</mat-checkbox>\n <mat-checkbox [(ngModel)]=\"bodyPreviewSideBySide\">bodyPreviewSideBySide</mat-checkbox>\n <mat-checkbox [(ngModel)]=\"debug\">debug</mat-checkbox>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"outline\" (click)=\"reset()\">Reset</ion-button>\n <ion-button fill=\"outline\" (click)=\"reset(true)\">Markdown sample</ion-button>\n <ion-button (click)=\"openModal()\">Open in a modal</ion-button>\n </ion-col>\n </ion-row>\n\n <!-- inline form -->\n <ion-row>\n <ion-col size=\"12\" size-lg=\"6\">\n <ion-card>\n <ion-card-content>\n <app-message-form\n #form\n [suggestFn]=\"suggestFn\"\n [canSelectType]=\"canSelectType\"\n [canAttach]=\"canAttach\"\n [bodyPreviewSideBySide]=\"bodyPreviewSideBySide\"\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [debug]=\"debug\"\n ></app-message-form>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- form value -->\n <ion-col size=\"12\" size-lg=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-subtitle>Form value (valid: {{ form.valid }})</ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <pre>{{ form.form?.value | json }}</pre>\n </ion-card-content>\n </ion-card>\n\n @if (modalResult) {\n <ion-card>\n <ion-card-header>\n <ion-card-subtitle>Modal result</ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <pre>{{ modalResult | json }}</pre>\n </ion-card-content>\n </ion-card>\n }\n </ion-col>\n </ion-row>\n </ion-grid>\n</ion-content>\n" }]
|
|
59546
|
+
}], ctorParameters: () => [{ type: i2$2.ModalController }, { type: i0.ChangeDetectorRef }], propDecorators: { form: [{
|
|
59547
|
+
type: ViewChild,
|
|
59548
|
+
args: ['form', { static: true }]
|
|
59549
|
+
}] } });
|
|
59550
|
+
|
|
59551
|
+
class MessageFormTestingModule {
|
|
59552
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageFormTestingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
59553
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: MessageFormTestingModule, declarations: [MessageFormTestingPage], imports: [SharedModule, FormsModule, MessageModule], exports: [MessageFormTestingPage] });
|
|
59554
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageFormTestingModule, providers: [provideChildTranslateService()], imports: [SharedModule, FormsModule, MessageModule] });
|
|
59555
|
+
}
|
|
59556
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MessageFormTestingModule, decorators: [{
|
|
59557
|
+
type: NgModule,
|
|
59558
|
+
args: [{
|
|
59559
|
+
imports: [SharedModule, FormsModule, MessageModule],
|
|
59560
|
+
declarations: [MessageFormTestingPage],
|
|
59561
|
+
exports: [MessageFormTestingPage],
|
|
59562
|
+
providers: [provideChildTranslateService()],
|
|
59563
|
+
}]
|
|
59564
|
+
}] });
|
|
59565
|
+
|
|
58765
59566
|
// Testing pages, declared outside the testing module, to be imported without loading the (lazy) testing module
|
|
58766
59567
|
const SOCIAL_TESTING_PAGES = [
|
|
58767
59568
|
{ label: 'Social components', divider: true },
|
|
58768
59569
|
{ label: 'Job progression', page: '/testing/social/job-progression' },
|
|
58769
59570
|
{ label: 'User event', page: '/testing/social/user-event' },
|
|
58770
59571
|
{ label: 'Feed', page: '/testing/social/feed' },
|
|
59572
|
+
{ label: 'Message form', page: '/testing/social/message-form' },
|
|
58771
59573
|
];
|
|
58772
59574
|
|
|
58773
59575
|
const routes = [
|
|
@@ -58786,6 +59588,11 @@ const routes = [
|
|
|
58786
59588
|
pathMatch: 'full',
|
|
58787
59589
|
component: FeedTestingPage,
|
|
58788
59590
|
},
|
|
59591
|
+
{
|
|
59592
|
+
path: 'message-form',
|
|
59593
|
+
pathMatch: 'full',
|
|
59594
|
+
component: MessageFormTestingPage,
|
|
59595
|
+
},
|
|
58789
59596
|
{
|
|
58790
59597
|
path: 'feed/page',
|
|
58791
59598
|
pathMatch: 'full',
|
|
@@ -58798,13 +59605,15 @@ class SocialTestingModule {
|
|
|
58798
59605
|
// Sub modules
|
|
58799
59606
|
JobTestingModule,
|
|
58800
59607
|
UserEventTestingModule,
|
|
58801
|
-
FeedTestingModule
|
|
59608
|
+
FeedTestingModule,
|
|
59609
|
+
MessageFormTestingModule] });
|
|
58802
59610
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SocialTestingModule, providers: [provideChildTranslateService()], imports: [CommonModule,
|
|
58803
59611
|
RouterModule.forChild(routes),
|
|
58804
59612
|
// Sub modules
|
|
58805
59613
|
JobTestingModule,
|
|
58806
59614
|
UserEventTestingModule,
|
|
58807
|
-
FeedTestingModule
|
|
59615
|
+
FeedTestingModule,
|
|
59616
|
+
MessageFormTestingModule] });
|
|
58808
59617
|
}
|
|
58809
59618
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SocialTestingModule, decorators: [{
|
|
58810
59619
|
type: NgModule,
|
|
@@ -58816,6 +59625,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
58816
59625
|
JobTestingModule,
|
|
58817
59626
|
UserEventTestingModule,
|
|
58818
59627
|
FeedTestingModule,
|
|
59628
|
+
MessageFormTestingModule,
|
|
58819
59629
|
],
|
|
58820
59630
|
providers: [provideChildTranslateService()],
|
|
58821
59631
|
}]
|
|
@@ -58856,5 +59666,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
58856
59666
|
* Generated bundle index. Do not edit.
|
|
58857
59667
|
*/
|
|
58858
59668
|
|
|
58859
|
-
export { ADMIN_ICONS, APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_ACCOUNT_SERVICE, APP_ACCOUNT_SERVICE_OPTIONS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FILE_TRANSFER_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_SERVICE, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOME_TOOLBAR_BUTTONS, APP_HOTKEYS_CONFIG, APP_INPUT_FIELD, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_MESSAGE_SERVICE_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PERSON_SERVICE, APP_PERSON_SERVICE_OPTIONS, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_SHOW_TOOLTIP, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TABLE_OPTIONS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractPersonService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppHomePageModule, AppIconComponent, AppIconModule, AppIconSelectorField, AppIconSelectorModal, AppIconSelectorModule, AppImageGalleryComponent, AppImageGallerySlideshowComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSelectUsersModal, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextFormModule, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppWebSocket, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFindPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutoTooltipDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_ICONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangeCaseToUnderscorePipe, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_ISO_PATTERNS, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateFromPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EmptyMissingTranslationHandler, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FILE_KIND_ICONS, FeedAttachmentUtils, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileAttachmentComponent, FileAttachmentPreviewModal, FileAttachmentUtils, FileDownloadService, FileResponse, FileService, FileSizePipe, FileTypeUtils, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayAtControlPipe, FormArrayAtGroupPipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetNamePipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GitlabUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, HttpUtils, IMAGE_DEFAULTS, ION_ICONS, IPosition, IconSelectorTestPage, IconSelectorTestingModule, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImageSizes, ImagesUtils, InMemoryEntitiesService, InputFieldTestPage, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, ListFormTestPage, ListFormTestingModule, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialAutocompleteFooterDirective, MaterialAutocompleteHeaderDirective, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageAttachment, MessageAttachmentUtils, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NativeWebSocket, NavActionsColumnComponent, NestedTableTestPage, NetworkService, NetworkUtils, NewTokenForm, NewTokenModal, NgInitDirective, NgVarDirective, NoHtmlPipe, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PLUS_PLACEHOLDER_CHAR_REGEXP_GLOBAL, PRINT_ID_QUERY_PARAM, PRINT_LOADING_STORAGE_KEY_PREFIX, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFilterAdditionalFields, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, PrintService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, ReferentialsToStringPipe, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, RxStateOutput, RxStateProperty, RxStateRegister, RxStateSelect, SCRYPT_PARAMS, SETTINGS_COMPACT_ROWS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_ICONS, SHARED_IONIC_COMPONENTS, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_ICONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, STARTUP_DATA_STORAGE_KEY, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFileAttachmentModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedIonicModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StartupService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StorageServiceWrapper, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, TRACKED_QUERIES_STORAGE_KEY, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, TimeFormatPipe, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, USER_CONTROLLER, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserController, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, assignSkipUndefined, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, collectByPropertyPath, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createApolloClientOptions, createAppStartupInitializer, createPromiseEvent, createPromiseEventEmitter, createTestingImageDataUrl, createTrackerLink, decorateWithTakeUntil, deepMergeSkipUndefined, departmentToString, departmentsToString, disableAndClearControl, disableAndClearControls, disableControl, disableControls, emitPromiseEvent, enableControl, enableControls, enableRxStateProdMode, entityToString, equals, equalsOrNil, escapeRegExp, expansionAnimation, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorLuminance, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormControlName, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hasFocusWithin, hexToRgb, hexToRgbArray, initArrayControlsFromValues, initializeSharedModule, interpolateString, intersectArrays, isAndroid, isAppInputField, isAppTextInputField, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isLightColor, isMacOS, isMobile, isMutationOperation, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isSubscriptionOperation, isTouchUi, isValidVersion, isVersionCompatible, isWebAnimationsSupported, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, loggerLink, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mergeObjectsWithoutUndefined, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseBooleanText, parseLatLongText, parseLatitudeOrLongitude, parseVersion, propertiesPathComparator, propertyComparator, propertyPathComparator, provideAccountService, provideAllIonicIcons, provideIonicIcons, provideIonicIconsInitializer, providePersonService, referentialToString, referentialsToString, registerIonicIcons, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, restoreTrackedQueries, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, setControlRequired, setControlsEnabled, setFormErrors, setPropertyByPath, setTabIndex, sleep, slideDownAnimation, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitArrayInChunks, splitById, splitByProperty, splitDegreesToDDArray, splitDegreesToDDMMArray, splitDegreesToDDMMSSArray, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toLoadData, toLoadResult, toNotNil, toNumber, trimEmptyToNull, truncateHtml, typeTextIntoInput, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
59669
|
+
export { ADMIN_ICONS, APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_ACCOUNT_SERVICE, APP_ACCOUNT_SERVICE_OPTIONS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FILE_TRANSFER_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_SERVICE, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOME_TOOLBAR_BUTTONS, APP_HOTKEYS_CONFIG, APP_INPUT_FIELD, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_MESSAGE_SERVICE_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PERSON_SERVICE, APP_PERSON_SERVICE_OPTIONS, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_SHOW_TOOLTIP, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TABLE_OPTIONS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractPersonService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppHomePageModule, AppIconComponent, AppIconModule, AppIconSelectorField, AppIconSelectorModal, AppIconSelectorModule, AppImageGalleryComponent, AppImageGallerySlideshowComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSelectUsersModal, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextFormModule, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppWebSocket, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFindPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutoTooltipDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_ICONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangeCaseToUnderscorePipe, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_ISO_PATTERNS, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DEFER_DIRECTIVE_NAME, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateFromPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EmptyMissingTranslationHandler, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FILE_KIND_ICONS, FeedAttachmentUtils, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileAttachmentComponent, FileAttachmentPreviewModal, FileAttachmentUtils, FileDownloadService, FileResponse, FileService, FileSizePipe, FileTypeUtils, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayAtControlPipe, FormArrayAtGroupPipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetNamePipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GitlabUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, HttpUtils, IMAGE_DEFAULTS, ION_ICONS, IPosition, IconSelectorTestPage, IconSelectorTestingModule, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImageSizes, ImagesUtils, InMemoryEntitiesService, InputFieldTestPage, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, ListFormTestPage, ListFormTestingModule, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownToolbarComponent, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialAutocompleteFooterDirective, MaterialAutocompleteHeaderDirective, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageAttachment, MessageAttachmentUtils, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NativeWebSocket, NavActionsColumnComponent, NestedTableTestPage, NetworkService, NetworkUtils, NewTokenForm, NewTokenModal, NgInitDirective, NgVarDirective, NoHtmlPipe, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PLUS_PLACEHOLDER_CHAR_REGEXP_GLOBAL, PRINT_ID_QUERY_PARAM, PRINT_LOADING_STORAGE_KEY_PREFIX, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFilterAdditionalFields, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, PrintService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, ReferentialsToStringPipe, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, RxStateOutput, RxStateProperty, RxStateRegister, RxStateSelect, SCRYPT_PARAMS, SETTINGS_COMPACT_ROWS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_ICONS, SHARED_IONIC_COMPONENTS, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_ICONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, STARTUP_DATA_STORAGE_KEY, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFileAttachmentModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedIonicModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StartupService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StorageServiceWrapper, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, TRACKED_QUERIES_STORAGE_KEY, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, TimeFormatPipe, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, USER_CONTROLLER, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserController, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, assignSkipUndefined, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, collectByPropertyPath, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, compositionMarkdownExtensions, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createApolloClientOptions, createAppStartupInitializer, createPromiseEvent, createPromiseEventEmitter, createTestingImageDataUrl, createTrackerLink, decorateWithTakeUntil, deepMergeSkipUndefined, departmentToString, departmentsToString, disableAndClearControl, disableAndClearControls, disableControl, disableControls, emitPromiseEvent, enableControl, enableControls, enableRxStateProdMode, entityToString, equals, equalsOrNil, escapeRegExp, expansionAnimation, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorLuminance, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormControlName, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hasDeferDirective, hasFocusWithin, hexToRgb, hexToRgbArray, httpHeadersToRecordLink, initArrayControlsFromValues, initializeSharedModule, insertLink, interpolateString, intersectArrays, isAndroid, isAppInputField, isAppTextInputField, isBlankString, isCapacitor, isChrome, isControlHasInput, isDeferredOperation, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isLightColor, isMacOS, isMobile, isMutationOperation, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isSubscriptionOperation, isTouchUi, isValidVersion, isVersionCompatible, isWebAnimationsSupported, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, loggerLink, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mergeObjectsWithoutUndefined, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseBooleanText, parseLatLongText, parseLatitudeOrLongitude, parseVersion, propertiesPathComparator, propertyComparator, propertyPathComparator, provideAccountService, provideAllIonicIcons, provideIonicIcons, provideIonicIconsInitializer, providePersonService, referentialToString, referentialsToString, registerIonicIcons, remove, removeAll, removeDeferDirectives, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, restoreTrackedQueries, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, setControlRequired, setControlsEnabled, setFormErrors, setPropertyByPath, setTabIndex, sleep, slideDownAnimation, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitArrayInChunks, splitById, splitByProperty, splitDegreesToDDArray, splitDegreesToDDMMArray, splitDegreesToDDMMSSArray, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toLoadData, toLoadResult, toNotNil, toNumber, toggleLinePrefix, trimEmptyToNull, truncateHtml, typeTextIntoInput, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending, wrapSelection };
|
|
58860
59670
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|