@sumaris-net/ngx-components 18.22.23 → 18.23.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/doc/changelog.md +9 -0
  2. package/esm2022/public_api.mjs +4 -1
  3. package/esm2022/src/app/core/table/column/actions-column.component.mjs +1 -1
  4. package/esm2022/src/app/core/table/table.pipes.mjs +6 -7
  5. package/esm2022/src/app/core/table/testing/table.testing.module.mjs +25 -4
  6. package/esm2022/src/app/core/table/testing/table2.testing.mjs +63 -15
  7. package/esm2022/src/app/shared/directives/cell-selection/cell-identifier.directive.mjs +41 -0
  8. package/esm2022/src/app/shared/directives/cell-selection/cell-selection.directive.mjs +201 -0
  9. package/esm2022/src/app/shared/directives/cell-selection/cell-selection.service.mjs +180 -0
  10. package/esm2022/src/app/shared/functions.mjs +1 -1
  11. package/esm2022/src/app/shared/material/datetime/material.date.mjs +4 -2
  12. package/esm2022/src/app/shared/material/datetime/material.datetime.mjs +5 -2
  13. package/esm2022/src/app/social/feed/feed.component.mjs +3 -3
  14. package/fesm2022/sumaris-net.ngx-components.mjs +497 -21
  15. package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
  16. package/package.json +1 -1
  17. package/public_api.d.ts +3 -0
  18. package/src/app/core/table/column/actions-column.component.d.ts +1 -1
  19. package/src/app/core/table/table.pipes.d.ts +2 -1
  20. package/src/app/core/table/testing/table.testing.module.d.ts +3 -1
  21. package/src/app/core/table/testing/table2.testing.d.ts +7 -3
  22. package/src/app/shared/directives/cell-selection/cell-identifier.directive.d.ts +15 -0
  23. package/src/app/shared/directives/cell-selection/cell-selection.directive.d.ts +28 -0
  24. package/src/app/shared/directives/cell-selection/cell-selection.service.d.ts +62 -0
  25. package/src/app/shared/functions.d.ts +1 -1
  26. package/src/app/shared/inputs.d.ts +1 -1
  27. package/src/theme/_ngx-components.table.scss +45 -2
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Directive, Pipe, Injectable, EventEmitter, Output, Optional, Inject, inject, NgModule, forwardRef, booleanAttribute, Component, ChangeDetectionStrategy, Input, ViewChildren, HostBinding, HostListener, ElementRef, numberAttribute, ViewChild, ANIMATION_MODULE_TYPE, RendererStyleFlags2, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Self, ViewEncapsulation, APP_INITIALIZER, SecurityContext, viewChild, signal, input, model, computed, effect, untracked } from '@angular/core';
2
+ import { InjectionToken, Directive, Pipe, Injectable, EventEmitter, Output, Optional, Inject, inject, NgModule, forwardRef, booleanAttribute, Component, ChangeDetectionStrategy, Input, ViewChildren, HostBinding, HostListener, ElementRef, numberAttribute, ViewChild, ANIMATION_MODULE_TYPE, RendererStyleFlags2, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Self, ViewEncapsulation, APP_INITIALIZER, input, output, Renderer2, DestroyRef, SecurityContext, viewChild, signal, model, computed, effect, untracked } from '@angular/core';
3
3
  import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, noop as noop$9, Observable, forkJoin, timeout, defer, combineLatest, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, interval, mergeMap as mergeMap$1, switchMap as switchMap$1, EMPTY } from 'rxjs';
4
4
  import { catchError, map, filter, takeUntil, first, switchMap, tap as tap$1, throttleTime, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, finalize, distinctUntilKeyChanged, bufferWhen, take } from 'rxjs/operators';
5
5
  import * as cloneImported from 'clone';
@@ -153,11 +153,11 @@ import * as i3$5 from '@angular/cdk/clipboard';
153
153
  import { select } from '@rx-angular/state/selections';
154
154
  import { isDataSource, SelectionModel } from '@angular/cdk/collections';
155
155
  import { Camera, CameraResultType } from '@capacitor/camera';
156
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
156
157
  import { Geolocation } from '@capacitor/geolocation';
157
158
  import * as i1$8 from '@e-is/ngx-material-table';
158
159
  import { ValidatorService, TableDataSource, AsyncTableDataSource } from '@e-is/ngx-material-table';
159
160
  import 'moment-timezone';
160
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
161
161
 
162
162
  const ENVIRONMENT = new InjectionToken('ENV');
163
163
  class Environment {
@@ -10437,7 +10437,9 @@ class MatDate {
10437
10437
  }
10438
10438
  clear(event) {
10439
10439
  this._preventEvent(event);
10440
- this.formControl.setValue(null);
10440
+ this.textControl.patchValue(null, { emitEvent: false });
10441
+ this.formControl.setValue(null, { emitEvent: false });
10442
+ this._onChangeCallback(null);
10441
10443
  this.markAsTouched();
10442
10444
  this.markAsDirty();
10443
10445
  }
@@ -10963,7 +10965,10 @@ class MatDateTime {
10963
10965
  }
10964
10966
  clear(event) {
10965
10967
  this._preventEvent(event);
10966
- this._formControl.setValue(null);
10968
+ this.dateControl.patchValue(null, { emitEvent: false });
10969
+ this.timeControl.patchValue(null, { emitEvent: false });
10970
+ this._formControl.setValue(null, { emitEvent: false });
10971
+ this._onChangeCallback(null);
10967
10972
  this.markAsTouched();
10968
10973
  this.markAsDirty();
10969
10974
  }
@@ -28359,6 +28364,414 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
28359
28364
  }]
28360
28365
  }] });
28361
28366
 
28367
+ const APP_CELL_SELECTION_SERVICE_TOKEN = new InjectionToken('CellSelectionService');
28368
+ const APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN = new InjectionToken('CellSelectionServiceConfig');
28369
+ /**
28370
+ * Service to manage cell selection state across multiple directive instances.
28371
+ * This service should be provided at the component level (not root) to ensure
28372
+ * proper isolation between different table instances.
28373
+ */
28374
+ class CellSelectionService {
28375
+ // Configuration
28376
+ config = inject(APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, { optional: true });
28377
+ selectableColumns = [];
28378
+ // Selection state
28379
+ selectedCells = [];
28380
+ selectionStartCell = null;
28381
+ isSelecting = false;
28382
+ // Drag state
28383
+ draggedCells = [];
28384
+ dragStartCell = null;
28385
+ isDragging = false;
28386
+ // Observables
28387
+ selectionChangeSubject = new Subject();
28388
+ selectionRightClickSubject = new Subject();
28389
+ // Overridable methods
28390
+ _equals;
28391
+ _new;
28392
+ constructor() {
28393
+ this._equals = this.config?.equals;
28394
+ this._new = this.config?.new;
28395
+ }
28396
+ // Override method
28397
+ equals(cell1, cell2) {
28398
+ return this._equals ? this._equals(cell1, cell2) : cell1?.rowId === cell2?.rowId && cell1?.columnName === cell2?.columnName;
28399
+ }
28400
+ // Override method
28401
+ new(cellId) {
28402
+ return this._new ? this._new(cellId) : cellId;
28403
+ }
28404
+ isCellSelected(cellId) {
28405
+ return this.selectedCells.some((selectedCell) => this.equals(selectedCell, cellId));
28406
+ }
28407
+ isCellDragged(cellId) {
28408
+ return this.draggedCells.some((selectedCell) => this.equals(selectedCell, cellId));
28409
+ }
28410
+ // Core selection logic (moved from CellIdentifierDirective)
28411
+ async handleCellMouseDown(event, cellId) {
28412
+ event.preventDefault();
28413
+ if (event.button === 2) {
28414
+ event.stopPropagation();
28415
+ this.selectionRightClickSubject.next(event);
28416
+ return;
28417
+ }
28418
+ const isCtrlKey = event.ctrlKey || event.metaKey;
28419
+ const isShiftKey = event.shiftKey;
28420
+ if (!isShiftKey && !isCtrlKey) {
28421
+ this.startSelection(cellId);
28422
+ // this.startDragSelection(cellId);
28423
+ }
28424
+ else if (isCtrlKey) {
28425
+ this.toggleCellSelection(cellId);
28426
+ }
28427
+ else if (isShiftKey) {
28428
+ this.selectRange(cellId);
28429
+ }
28430
+ }
28431
+ async handleCellMouseEnter(event, cellId) {
28432
+ // TODO check if next cell can be selected
28433
+ if (this.isSelecting && this.selectionStartCell) {
28434
+ this.updateSelection(cellId);
28435
+ }
28436
+ else if (this.isDragging && this.dragStartCell) {
28437
+ this.updateDraggedCells(cellId);
28438
+ }
28439
+ }
28440
+ endMouseMove() {
28441
+ if (this.isSelecting) {
28442
+ this.resetSelectionState();
28443
+ }
28444
+ else if (this.isDragging) {
28445
+ this.resetDragState();
28446
+ }
28447
+ }
28448
+ // Private helper methods
28449
+ startSelection(cellId) {
28450
+ this.selectionStartCell = cellId;
28451
+ this.isSelecting = true;
28452
+ this.emitSelection([cellId], false, false);
28453
+ }
28454
+ startDragSelection(cellId) {
28455
+ this.dragStartCell = cellId;
28456
+ this.isDragging = true;
28457
+ this.draggedCells = [cellId];
28458
+ // this.emitSelection([cellId], false, false);
28459
+ }
28460
+ toggleCellSelection(cellId) {
28461
+ const currentCells = [...this.selectedCells];
28462
+ const cellIndex = currentCells.findIndex((c) => this.equals(c, cellId));
28463
+ if (cellIndex >= 0) {
28464
+ currentCells.splice(cellIndex, 1);
28465
+ }
28466
+ else {
28467
+ currentCells.push(cellId);
28468
+ }
28469
+ this.emitSelection(currentCells, false, true);
28470
+ }
28471
+ selectRange(endCell) {
28472
+ const rangeCells = this.calculateRangeCells(endCell);
28473
+ this.emitSelection(rangeCells, true, false);
28474
+ }
28475
+ updateSelection(currentCell) {
28476
+ const rangeCells = this.calculateRangeBetweenCells(this.selectionStartCell, currentCell);
28477
+ this.emitSelection(rangeCells, true, false);
28478
+ }
28479
+ updateDraggedCells(currentCell) {
28480
+ // this.draggedCells.set(rangeCells);
28481
+ }
28482
+ emitSelection(cells, isRange, isToggle) {
28483
+ this.selectedCells = cells;
28484
+ this.selectionChangeSubject.next({
28485
+ selectedCells: cells,
28486
+ isRangeSelection: isRange,
28487
+ isToggleSelection: isToggle,
28488
+ });
28489
+ }
28490
+ calculateRangeCells(endCell) {
28491
+ if (isEmptyArray(this.selectedCells)) {
28492
+ return [endCell];
28493
+ }
28494
+ const lastSelectedCell = this.findFirstSelectedCell(this.selectedCells);
28495
+ return this.calculateRangeBetweenCells(lastSelectedCell, endCell);
28496
+ }
28497
+ calculateRangeBetweenCells(startCell, endCell) {
28498
+ // Get first cell coordinates, then add this cell as a range
28499
+ const firstRowId = Math.min(startCell.rowId, endCell.rowId);
28500
+ const lastRowId = Math.max(startCell.rowId, endCell.rowId);
28501
+ const startColumnIndex = this.selectableColumns.indexOf(startCell.columnName);
28502
+ const endColumnIndex = this.selectableColumns.indexOf(endCell.columnName);
28503
+ const firstColumnIndex = Math.min(startColumnIndex, endColumnIndex);
28504
+ const lastColumnIndex = Math.max(startColumnIndex, endColumnIndex);
28505
+ // Iterate columns and rows
28506
+ const rangeCells = [];
28507
+ for (let i = firstColumnIndex; i <= lastColumnIndex; i++) {
28508
+ const columnName = this.selectableColumns[i];
28509
+ for (let rowId = firstRowId; rowId <= lastRowId; rowId++) {
28510
+ rangeCells.push(this.new({ rowId: rowId, columnName: columnName }));
28511
+ }
28512
+ }
28513
+ return rangeCells;
28514
+ }
28515
+ resetSelectionState() {
28516
+ this.selectionStartCell = null;
28517
+ this.isSelecting = false;
28518
+ }
28519
+ resetDragState() {
28520
+ this.dragStartCell = null;
28521
+ this.isDragging = false;
28522
+ this.draggedCells = [];
28523
+ }
28524
+ findFirstSelectedCell(selectedCells) {
28525
+ // Find the cell with the lowest rowId and columnIndex
28526
+ return this.findSelectedCell(selectedCells, Math.min(...selectedCells.map((cell) => cell.rowId)), Math.min(...selectedCells.map((cell) => this.selectableColumns.indexOf(cell.columnName))));
28527
+ }
28528
+ findLastSelectedCell(selectedCells) {
28529
+ // Find the cell with the lowest rowId and columnIndex
28530
+ return this.findSelectedCell(selectedCells, Math.max(...selectedCells.map((cell) => cell.rowId)), Math.max(...selectedCells.map((cell) => this.selectableColumns.indexOf(cell.columnName))));
28531
+ }
28532
+ findSelectedCell(selectedCells, rowId, columnIndex) {
28533
+ // Find the cell with the current indexes
28534
+ return selectedCells.filter((cell) => cell.rowId === rowId && cell.columnName === this.selectableColumns[columnIndex])[0];
28535
+ }
28536
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
28537
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellSelectionService });
28538
+ }
28539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellSelectionService, decorators: [{
28540
+ type: Injectable
28541
+ }], ctorParameters: () => [] });
28542
+
28543
+ class CellSelectionDirective {
28544
+ // Inputs
28545
+ // eslint-disable-next-line @angular-eslint/no-input-rename
28546
+ selectableColumns = input.required({ alias: 'appSelectableColumns' });
28547
+ // Outputs
28548
+ // eslint-disable-next-line @angular-eslint/no-output-rename
28549
+ selectionChange = output({ alias: 'appCellSelectionChange' });
28550
+ // eslint-disable-next-line @angular-eslint/no-output-rename
28551
+ rightClick = output({ alias: 'appCellRightClick' });
28552
+ // Inject service by token
28553
+ service = inject(APP_CELL_SELECTION_SERVICE_TOKEN);
28554
+ elementRef = inject(ElementRef);
28555
+ renderer = inject(Renderer2);
28556
+ destroyRef = inject(DestroyRef);
28557
+ overlayElements = [];
28558
+ constructor() { }
28559
+ ngOnInit() {
28560
+ // Initialize service with selectable columns
28561
+ this.setupServiceConfiguration();
28562
+ // Setup global mouseup listener
28563
+ this.setupGlobalMouseEvents();
28564
+ // Setup keyboard listener for copy (for test)
28565
+ // this.setupCopyListener();
28566
+ }
28567
+ setupServiceConfiguration() {
28568
+ // Pass selectable columns to service
28569
+ this.service.selectableColumns = this.selectableColumns();
28570
+ // Listen to service selection changes and emit
28571
+ this.service.selectionChangeSubject
28572
+ .pipe(takeUntilDestroyed(this.destroyRef))
28573
+ .subscribe((event) => this.selectionChange.emit(event));
28574
+ // Listen to right-click event
28575
+ this.service.selectionRightClickSubject.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => this.rightClick.emit(event));
28576
+ }
28577
+ setupGlobalMouseEvents() {
28578
+ fromEvent(document, 'mouseup')
28579
+ .pipe(takeUntilDestroyed(this.destroyRef))
28580
+ .subscribe(() => this.service.endMouseMove());
28581
+ }
28582
+ // For test purpose only (type CTRL+C)
28583
+ setupCopyListener() {
28584
+ fromEvent(document, 'keydown')
28585
+ .pipe(takeUntilDestroyed(this.destroyRef))
28586
+ .subscribe((event) => {
28587
+ if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
28588
+ const cells = this.service.selectedCells;
28589
+ if (isNotEmptyArray(cells)) {
28590
+ // Show overlay
28591
+ this.addCellsOverlay(cells);
28592
+ event.preventDefault();
28593
+ // Hide overlay after animation completes (1 second)
28594
+ setTimeout(() => {
28595
+ this.removeCellsOverlay();
28596
+ }, 1000);
28597
+ }
28598
+ }
28599
+ });
28600
+ }
28601
+ addCellsOverlay(cells) {
28602
+ // Remove existing overlays
28603
+ this.removeCellsOverlay();
28604
+ // Group cells into contiguous regions
28605
+ const cellGroups = this.groupContiguousCells(cells);
28606
+ // Create and render overlay for each contiguous region
28607
+ cellGroups.forEach((group) => {
28608
+ const bounds = this.calculateSelectionBounds(group);
28609
+ if (bounds) {
28610
+ const overlay = this.createOverlayElement(bounds);
28611
+ this.renderer.appendChild(this.elementRef.nativeElement, overlay);
28612
+ this.overlayElements.push(overlay);
28613
+ }
28614
+ });
28615
+ }
28616
+ removeCellsOverlay() {
28617
+ this.overlayElements.forEach((overlay) => {
28618
+ this.renderer.removeChild(this.elementRef.nativeElement, overlay);
28619
+ });
28620
+ this.overlayElements = [];
28621
+ }
28622
+ groupContiguousCells(cells) {
28623
+ // Create a map for quick lookup
28624
+ const cellMap = new Map();
28625
+ cells.forEach((cell) => {
28626
+ cellMap.set(this.getCellKey(cell), cell);
28627
+ });
28628
+ const visited = new Set();
28629
+ const groups = [];
28630
+ // Process each cell
28631
+ cells.forEach((cell) => {
28632
+ const key = this.getCellKey(cell);
28633
+ if (visited.has(key)) {
28634
+ return;
28635
+ }
28636
+ // Start a new contiguous group with flood-fill
28637
+ const group = [];
28638
+ const queue = [cell];
28639
+ visited.add(key);
28640
+ while (queue.length > 0) {
28641
+ const current = queue.shift();
28642
+ group.push(current);
28643
+ // Check all 4 adjacent cells (up, down, left, right)
28644
+ const neighbors = this.getAdjacentCells(current);
28645
+ neighbors.forEach((neighbor) => {
28646
+ const neighborKey = this.getCellKey(neighbor);
28647
+ if (cellMap.has(neighborKey) && !visited.has(neighborKey)) {
28648
+ visited.add(neighborKey);
28649
+ queue.push(neighbor);
28650
+ }
28651
+ });
28652
+ }
28653
+ groups.push(group);
28654
+ });
28655
+ return groups;
28656
+ }
28657
+ getAdjacentCells(cell) {
28658
+ // Returns cells adjacent to the current cell (up, down, left, right)
28659
+ // Note: We need to get column order from the actual table structure
28660
+ // For now, we'll use a simple approach based on rowId adjacency
28661
+ return [
28662
+ { rowId: cell.rowId - 1, columnName: cell.columnName }, // up
28663
+ { rowId: cell.rowId + 1, columnName: cell.columnName }, // down
28664
+ { rowId: cell.rowId, columnName: this.getNextColumnName(cell.columnName, -1) }, // left
28665
+ { rowId: cell.rowId, columnName: this.getNextColumnName(cell.columnName, 1) }, // right
28666
+ ].filter((c) => c.columnName !== null);
28667
+ }
28668
+ getNextColumnName(currentColumnName, offset) {
28669
+ const columns = this.service.selectableColumns;
28670
+ const currentIndex = columns.indexOf(currentColumnName);
28671
+ if (currentIndex === -1) {
28672
+ return null;
28673
+ }
28674
+ const newIndex = currentIndex + offset;
28675
+ if (newIndex < 0 || newIndex >= columns.length) {
28676
+ return null;
28677
+ }
28678
+ return columns[newIndex];
28679
+ }
28680
+ getCellKey(cell) {
28681
+ return `${cell.rowId}-${cell.columnName}`;
28682
+ }
28683
+ calculateSelectionBounds(cells) {
28684
+ const container = this.elementRef.nativeElement;
28685
+ // Find all selected cell elements
28686
+ const cellElements = [];
28687
+ cells.forEach((cell) => {
28688
+ const cellElement = container.querySelector(`[data-row-id="${cell.rowId}"][data-column-name="${cell.columnName}"]`);
28689
+ if (cellElement) {
28690
+ cellElements.push(cellElement);
28691
+ }
28692
+ });
28693
+ if (isEmptyArray(cellElements)) {
28694
+ return null;
28695
+ }
28696
+ // Get container position
28697
+ const containerRect = container.getBoundingClientRect();
28698
+ // Calculate bounding box
28699
+ let minTop = Infinity;
28700
+ let minLeft = Infinity;
28701
+ let maxBottom = -Infinity;
28702
+ let maxRight = -Infinity;
28703
+ cellElements.forEach((element) => {
28704
+ const rect = element.getBoundingClientRect();
28705
+ minTop = Math.min(minTop, rect.top - containerRect.top);
28706
+ minLeft = Math.min(minLeft, rect.left - containerRect.left);
28707
+ maxBottom = Math.max(maxBottom, rect.bottom - containerRect.top);
28708
+ maxRight = Math.max(maxRight, rect.right - containerRect.left);
28709
+ });
28710
+ return {
28711
+ top: minTop,
28712
+ left: minLeft,
28713
+ width: maxRight - minLeft,
28714
+ height: maxBottom - minTop,
28715
+ };
28716
+ }
28717
+ createOverlayElement(bounds) {
28718
+ const overlay = this.renderer.createElement('div');
28719
+ this.renderer.addClass(overlay, 'cell-selection-overlay');
28720
+ // Set position and size
28721
+ this.renderer.setStyle(overlay, 'top', `${bounds.top}px`);
28722
+ this.renderer.setStyle(overlay, 'left', `${bounds.left}px`);
28723
+ this.renderer.setStyle(overlay, 'width', `${bounds.width}px`);
28724
+ this.renderer.setStyle(overlay, 'height', `${bounds.height}px`);
28725
+ return overlay;
28726
+ }
28727
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellSelectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
28728
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.13", type: CellSelectionDirective, isStandalone: true, selector: "[appCellSelection]", inputs: { selectableColumns: { classPropertyName: "selectableColumns", publicName: "appSelectableColumns", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { selectionChange: "appCellSelectionChange", rightClick: "appCellRightClick" }, ngImport: i0 });
28729
+ }
28730
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellSelectionDirective, decorators: [{
28731
+ type: Directive,
28732
+ args: [{
28733
+ selector: '[appCellSelection]',
28734
+ standalone: true,
28735
+ }]
28736
+ }], ctorParameters: () => [] });
28737
+
28738
+ class CellIdentifierDirective {
28739
+ cellId = input.required({ alias: 'appCellId' });
28740
+ isCellSelected = false;
28741
+ isDragging = false;
28742
+ service = inject(APP_CELL_SELECTION_SERVICE_TOKEN);
28743
+ destroyRef = inject(DestroyRef);
28744
+ ngOnInit() {
28745
+ // Listen to service selection changes and emit
28746
+ this.service.selectionChangeSubject
28747
+ .pipe(takeUntilDestroyed(this.destroyRef))
28748
+ .subscribe(() => (this.isCellSelected = this.service.isCellSelected(this.cellId())));
28749
+ }
28750
+ async handleMouseDown(event) {
28751
+ await this.service.handleCellMouseDown(event, this.cellId());
28752
+ }
28753
+ async handleMouseEnter(event) {
28754
+ await this.service.handleCellMouseEnter(event, this.cellId());
28755
+ }
28756
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellIdentifierDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
28757
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.13", type: CellIdentifierDirective, isStandalone: true, selector: "[appCellId]", inputs: { cellId: { classPropertyName: "cellId", publicName: "appCellId", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mousedown": "handleMouseDown($event)", "mouseenter": "handleMouseEnter($event)" }, properties: { "class.cell-selected": "isCellSelected", "class.cell-dragging": "isDragging", "attr.data-row-id": "cellId().rowId", "attr.data-column-name": "cellId().columnName" } }, ngImport: i0 });
28758
+ }
28759
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CellIdentifierDirective, decorators: [{
28760
+ type: Directive,
28761
+ args: [{
28762
+ selector: '[appCellId]',
28763
+ standalone: true,
28764
+ host: {
28765
+ '(mousedown)': 'handleMouseDown($event)',
28766
+ '(mouseenter)': 'handleMouseEnter($event)',
28767
+ '[class.cell-selected]': 'isCellSelected',
28768
+ '[class.cell-dragging]': 'isDragging',
28769
+ '[attr.data-row-id]': 'cellId().rowId',
28770
+ '[attr.data-column-name]': 'cellId().columnName',
28771
+ },
28772
+ }]
28773
+ }] });
28774
+
28362
28775
  /**
28363
28776
  * Merges two LoadResult objects into a single LoadResult object by combining their data arrays and summing their total counts.
28364
28777
  *
@@ -35688,11 +36101,11 @@ class FeedsComponent {
35688
36101
  }
35689
36102
  }
35690
36103
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT }, { token: APP_FEED_SERVICE, optional: true }, { token: AccountService }, { token: i2$1.AlertController }], target: i0.ɵɵFactoryTarget.Component });
35691
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedsComponent, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-label translate>SOCIAL.FEED.SHOW_ALL_FEED</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n &nbsp;\n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button\n (click)=\"onEditItem($event, item)\"\n class=\"visible-hover ion-float-start\" fill=\"clear\"\n >\n <mat-icon slot=\"start\">edit</mat-icon>\n<!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-label translate>COMMON.BTN_EDIT</ion-label>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 1px 5px rgba(0,0,0,.12);-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}\n"], dependencies: [{ kind: "component", type: i2$1.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$1.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$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$1.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$1.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonModal, selector: "ion-modal" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: i4$2.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: i6$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i11$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: ArrayFilterPipe, name: "arrayFilter" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
36104
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedsComponent, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-label translate>SOCIAL.FEED.SHOW_ALL_FEED</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n &nbsp;\n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button\n (click)=\"onEditItem($event, item)\"\n class=\"visible-hover ion-float-start\" fill=\"clear\"\n >\n <mat-icon slot=\"start\">edit</mat-icon>\n<!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-label translate>COMMON.BTN_EDIT</ion-label>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}\n"], dependencies: [{ kind: "component", type: i2$1.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$1.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$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$1.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$1.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonModal, selector: "ion-modal" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: i4$2.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: i6$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i11$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: ArrayFilterPipe, name: "arrayFilter" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
35692
36105
  }
35693
36106
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, decorators: [{
35694
36107
  type: Component,
35695
- args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-label translate>SOCIAL.FEED.SHOW_ALL_FEED</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n &nbsp;\n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button\n (click)=\"onEditItem($event, item)\"\n class=\"visible-hover ion-float-start\" fill=\"clear\"\n >\n <mat-icon slot=\"start\">edit</mat-icon>\n<!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-label translate>COMMON.BTN_EDIT</ion-label>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 1px 5px rgba(0,0,0,.12);-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}\n"] }]
36108
+ args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-label translate>SOCIAL.FEED.SHOW_ALL_FEED</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n &nbsp;\n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button\n (click)=\"onEditItem($event, item)\"\n class=\"visible-hover ion-float-start\" fill=\"clear\"\n >\n <mat-icon slot=\"start\">edit</mat-icon>\n<!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-label translate>COMMON.BTN_EDIT</ion-label>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}\n"] }]
35696
36109
  }], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
35697
36110
  type: Inject,
35698
36111
  args: [ENVIRONMENT]
@@ -40387,14 +40800,14 @@ class AbstractTableSelectionPipe extends AbstractSelectionModelPipe {
40387
40800
  super(_ref);
40388
40801
  }
40389
40802
  transform(selectionOrTable, tableOrOpts, opts) {
40390
- // @ts-ignore
40391
- const _table = tableOrOpts instanceof (AppTable) ? tableOrOpts : selectionOrTable instanceof (AppTable) ? selectionOrTable : undefined;
40392
- // @ts-ignore
40803
+ const _table = this.isTable(tableOrOpts) ? tableOrOpts : this.isTable(selectionOrTable) ? selectionOrTable : undefined;
40393
40804
  const _selection = _table?.selection || selectionOrTable;
40394
- // @ts-ignore
40395
- const _opts = opts || !(tableOrOpts instanceof (AppTable) && tableOrOpts);
40805
+ const _opts = opts || (!this.isTable(tableOrOpts) && tableOrOpts);
40396
40806
  return super.transform(_selection, _table, _opts);
40397
40807
  }
40808
+ isTable(obj) {
40809
+ return obj && typeof obj === 'object' && 'dataSource' in obj && 'selection' in obj;
40810
+ }
40398
40811
  _subscribe(selection, table, opts) {
40399
40812
  this._onRowsChanges = table?.dataSource?.rowsSubject?.subscribe((_) => {
40400
40813
  const result = this._transform(selection, table, opts);
@@ -50132,7 +50545,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
50132
50545
  type: Injectable
50133
50546
  }], ctorParameters: () => [{ type: i1$3.UntypedFormBuilder }] });
50134
50547
 
50135
- class Table2TestPage extends AppTable {
50548
+ class Table2TestPage extends AppAsyncTable {
50136
50549
  validatorService;
50137
50550
  formBuilder;
50138
50551
  static maxRowCount = 100;
@@ -50152,6 +50565,7 @@ class Table2TestPage extends AppTable {
50152
50565
  }
50153
50566
  filterExpansionPanel;
50154
50567
  infiniteScroll;
50568
+ selectableColumns = ['name', 'levelId', 'boolean'];
50155
50569
  get dataService() {
50156
50570
  return this._dataSource.dataService;
50157
50571
  }
@@ -50164,7 +50578,7 @@ class Table2TestPage extends AppTable {
50164
50578
  'boolean2',
50165
50579
  //'boolean3',
50166
50580
  ...RESERVED_END_COLUMNS,
50167
- ], new EntitiesTableDataSource(Referential, new InMemoryEntitiesService(Referential, ReferentialFilter, {
50581
+ ], new EntitiesAsyncTableDataSource(Referential, new InMemoryEntitiesService(Referential, ReferentialFilter, {
50168
50582
  sortByReplacement: { id: 'id' },
50169
50583
  }), validatorService, {
50170
50584
  suppressErrors: false,
@@ -50290,9 +50704,9 @@ class Table2TestPage extends AppTable {
50290
50704
  this.markForCheck();
50291
50705
  }
50292
50706
  }
50293
- resetFilter(_) {
50707
+ async resetFilter(event) {
50294
50708
  this.filterForm.reset({}, { emitEvent: true });
50295
- this.setFilter(ReferentialFilter.fromObject({}), { emitEvent: true });
50709
+ await this.setFilter(ReferentialFilter.fromObject({}), { emitEvent: true });
50296
50710
  this.filterExpansionPanel.close();
50297
50711
  }
50298
50712
  generateData(offset, size) {
@@ -50317,6 +50731,17 @@ class Table2TestPage extends AppTable {
50317
50731
  }
50318
50732
  return result;
50319
50733
  }
50734
+ onCellSelectionChange(event) {
50735
+ this.markForCheck();
50736
+ console.debug('[table2-test] Selection changed:', {
50737
+ selectedCells: event.selectedCells,
50738
+ isRange: event.isRangeSelection,
50739
+ isToggle: event.isToggleSelection,
50740
+ });
50741
+ }
50742
+ onCellRightClick(event) {
50743
+ console.debug('[table2-test] Right click on cell:', event);
50744
+ }
50320
50745
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: Table2TestPage, deps: [{ token: i0.Injector }, { token: AppValidatorService }, { token: i1$3.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component });
50321
50746
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: Table2TestPage, selector: "app-table2-testing", inputs: { filterPanelFloating: "filterPanelFloating", sticky: "sticky", stickyEnd: "stickyEnd" }, providers: [
50322
50747
  {
@@ -50327,7 +50752,23 @@ class Table2TestPage extends AppTable {
50327
50752
  provide: AppTable,
50328
50753
  useExisting: forwardRef(() => Table2TestPage),
50329
50754
  },
50330
- ], 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 <button mat-icon-button *ngIf=\"canEdit && !mobile\" [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"addRow()\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- reset filter -->\n <button mat-icon-button (click)=\"resetFilter()\" *ngIf=\"filterCriteriaCount\">\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 <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n <mat-icon\n *ngIf=\"filterCriteriaCount; else emptyFilter\"\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 <ng-template #emptyFilter>\n <mat-icon>filter_list_alt</mat-icon>\n </ng-template>\n </button>\n\n <!-- save -->\n <button mat-icon-button *ngIf=\"mobile\" [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n <button\n mat-icon-button\n *ngIf=\"canEdit\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n\n <!-- duplicate -->\n <button\n mat-icon-button\n *ngIf=\"canEdit && selection.selected | isArrayLength: { equals: 1 }\"\n [title]=\"'COMMON.BTN_DUPLICATE' | translate\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n <ion-refresher slot=\"fixed\" *ngIf=\"mobile\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n\n <!-- error -->\n <ion-item *ngIf=\"mobile && error\" lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\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 <button\n mat-icon-button\n color=\"accent\"\n *ngIf=\"filterPanelFloating\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"(filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate\"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '&#xbb;' : '&#xab;' }}</span>\n </mat-icon>\n </button>\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text translate>COMMON.BTN_CLOSE</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 translate>COMMON.BTN_APPLY</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 <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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-form-field *ngIf=\"!readOnly && row.id === -1 && row.editing; else readOnlyId\">\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('alreadyExists')\" translate>\n ERROR.FIELD_NOT_UNIQUE_ID\n </mat-error>\n </mat-form-field>\n\n <ng-template #readOnlyId>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n </ng-template>\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 [title]=\"'TABLE.TESTING.NAME' | translate\">{{ 'TABLE.TESTING.NAME' | translate }}</ion-label>\n <ion-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'name'\">\n <mat-form-field *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\">\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 <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('maxlength')\" translate>\n ERROR.FIELD_MAX_LENGTH_COMPACT\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('minlength')\" translate>\n ERROR.FIELD_MIN_LENGTH_COMPACT\n </mat-error>\n </mat-form-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n </ng-template>\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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-autocomplete-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <mat-error matError *ngIf=\"(row.validator | formGetControl: 'levelId').hasError('invalid')\" translate>\n ERROR.FIELD_INVALID\n </mat-error>\n </mat-autocomplete-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\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 </ng-template>\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\">\n <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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\">\n <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <span *ngIf=\"row.editing && !row.validator.dirty\">-</span>\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 <ng-container *ngIf=\"loadingSubject | async; else noResult\">\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n </ng-container>\n\n <ng-template #noResult>\n <ion-item *ngIf=\"totalRowCount === 0\">\n <ion-text color=\"danger\" class=\"text-italic\" translate>COMMON.NO_RESULT</ion-text>\n </ion-item>\n </ng-template>\n\n <ion-infinite-scroll\n *ngIf=\"enableInfiniteScroll\"\n [threshold]=\"mobile ? '10%' : '2%'\"\n position=\"bottom\"\n (ionInfinite)=\"fetchMore($event)\"\n >\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n <mat-paginator\n *ngIf=\"!enableInfiniteScroll\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n\n <app-form-buttons-bar\n *ngIf=\"canEdit && !mobile\"\n (onCancel)=\"load()\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) || !dirty\"\n >\n <!-- error -->\n <ion-item *ngIf=\"error$ | async\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </app-form-buttons-bar>\n</ion-footer>\n\n<ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\" *ngIf=\"canEdit && mobile\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n</ion-fab>\n", styles: [".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: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.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$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.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$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$1.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$1.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$1.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$1.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$1.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.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$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i6$4.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: i1$7.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$7.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$7.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$7.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$7.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$7.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$7.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$7.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$7.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$7.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i8$2.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i8$2.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i9$3.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"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i13$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i13$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i11$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: i9.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$5.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "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", "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: AutoTitleDirective, selector: "ion-label[appAutoTitle], mat-label[appAutoTitle]", inputs: ["appAutoTitle"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "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$3.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]", outputs: ["resizable", "fit"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.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 });
50755
+ // Provide options :
50756
+ // {
50757
+ // provide: APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN,
50758
+ // useFactory: () => {
50759
+ // return <CellSelectionServiceConfig<ICellId>>{
50760
+ // equals: (cell1, cell2) => {
50761
+ // console.debug('[table2-test] Comparing cell ids:', cell1, cell2);
50762
+ // return cell1?.rowId === cell2?.rowId && cell1?.columnName === cell2?.columnName;
50763
+ // },
50764
+ // };
50765
+ // },
50766
+ // },
50767
+ {
50768
+ provide: APP_CELL_SELECTION_SERVICE_TOKEN,
50769
+ useClass: CellSelectionService,
50770
+ },
50771
+ ], 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 <button mat-icon-button *ngIf=\"canEdit && !mobile\" [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"addRow()\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- reset filter -->\n <button mat-icon-button (click)=\"resetFilter()\" *ngIf=\"filterCriteriaCount\">\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 <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n <mat-icon\n *ngIf=\"filterCriteriaCount; else emptyFilter\"\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 <ng-template #emptyFilter>\n <mat-icon>filter_list_alt</mat-icon>\n </ng-template>\n </button>\n\n <!-- save -->\n <button mat-icon-button *ngIf=\"mobile\" [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n <button\n mat-icon-button\n *ngIf=\"canEdit\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n\n <!-- duplicate -->\n <button\n mat-icon-button\n *ngIf=\"canEdit && selection.selected | isArrayLength: { equals: 1 }\"\n [title]=\"'COMMON.BTN_DUPLICATE' | translate\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n <ion-refresher slot=\"fixed\" *ngIf=\"mobile\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n\n <!-- error -->\n <ion-item *ngIf=\"mobile && error\" lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\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 <button\n mat-icon-button\n color=\"accent\"\n *ngIf=\"filterPanelFloating\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"(filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate\"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '&#xbb;' : '&#xab;' }}</span>\n </mat-icon>\n </button>\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text translate>COMMON.BTN_CLOSE</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 translate>COMMON.BTN_APPLY</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 (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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-form-field *ngIf=\"!readOnly && row.id === -1 && row.editing; else readOnlyId\">\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('alreadyExists')\" translate>\n ERROR.FIELD_NOT_UNIQUE_ID\n </mat-error>\n </mat-form-field>\n\n <ng-template #readOnlyId>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n </ng-template>\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 [title]=\"'TABLE.TESTING.NAME' | translate\">{{ 'TABLE.TESTING.NAME' | translate }}</ion-label>\n <ion-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\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 <mat-form-field *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\">\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 <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('maxlength')\" translate>\n ERROR.FIELD_MAX_LENGTH_COMPACT\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('minlength')\" translate>\n ERROR.FIELD_MIN_LENGTH_COMPACT\n </mat-error>\n </mat-form-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n </ng-template>\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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'levelId' }\">\n <mat-autocomplete-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <mat-error matError *ngIf=\"(row.validator | formGetControl: 'levelId').hasError('invalid')\" translate>\n ERROR.FIELD_INVALID\n </mat-error>\n </mat-autocomplete-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\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 </ng-template>\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 <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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\">\n <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <span *ngIf=\"row.editing && !row.validator.dirty\">-</span>\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 <ng-container *ngIf=\"loadingSubject | async; else noResult\">\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n </ng-container>\n\n <ng-template #noResult>\n <ion-item *ngIf=\"totalRowCount === 0\">\n <ion-text color=\"danger\" class=\"text-italic\" translate>COMMON.NO_RESULT</ion-text>\n </ion-item>\n </ng-template>\n\n <ion-infinite-scroll\n *ngIf=\"enableInfiniteScroll\"\n [threshold]=\"mobile ? '10%' : '2%'\"\n position=\"bottom\"\n (ionInfinite)=\"fetchMore($event)\"\n >\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n <mat-paginator\n *ngIf=\"!enableInfiniteScroll\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n\n <app-form-buttons-bar\n *ngIf=\"canEdit && !mobile\"\n (onCancel)=\"load()\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) || !dirty\"\n >\n <!-- error -->\n <ion-item *ngIf=\"errorSubject | async\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </app-form-buttons-bar>\n</ion-footer>\n\n<ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\" *ngIf=\"canEdit && mobile\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n</ion-fab>\n", styles: [".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: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.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$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.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$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonFab, selector: "ion-fab", inputs: ["activated", "edge", "horizontal", "vertical"] }, { kind: "component", type: i2$1.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i2$1.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: i2$1.IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: i2$1.IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: i2$1.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.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$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i6$4.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: i1$7.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$7.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$7.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$7.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$7.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$7.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$7.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$7.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$7.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$7.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i8$2.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i8$2.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: i9$3.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"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i13$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i13$2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i6$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i11$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: i9.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: i1$5.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: MatAutocompleteField, selector: "mat-autocomplete-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "label", "appearance", "subscriptSizing", "placeholder", "suggestFn", "required", "hideRequiredMarker", "mobile", "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", "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: AutoTitleDirective, selector: "ion-label[appAutoTitle], mat-label[appAutoTitle]", inputs: ["appAutoTitle"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "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$3.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]", outputs: ["resizable", "fit"] }, { kind: "directive", type: CellIdentifierDirective, selector: "[appCellId]", inputs: ["appCellId"] }, { kind: "directive", type: CellSelectionDirective, selector: "[appCellSelection]", inputs: ["appSelectableColumns"], outputs: ["appCellSelectionChange", "appCellRightClick"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.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 });
50331
50772
  }
50332
50773
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: Table2TestPage, decorators: [{
50333
50774
  type: Component,
@@ -50340,7 +50781,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
50340
50781
  provide: AppTable,
50341
50782
  useExisting: forwardRef(() => Table2TestPage),
50342
50783
  },
50343
- ], changeDetection: ChangeDetectionStrategy.OnPush, 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 <button mat-icon-button *ngIf=\"canEdit && !mobile\" [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"addRow()\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- reset filter -->\n <button mat-icon-button (click)=\"resetFilter()\" *ngIf=\"filterCriteriaCount\">\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 <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n <mat-icon\n *ngIf=\"filterCriteriaCount; else emptyFilter\"\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 <ng-template #emptyFilter>\n <mat-icon>filter_list_alt</mat-icon>\n </ng-template>\n </button>\n\n <!-- save -->\n <button mat-icon-button *ngIf=\"mobile\" [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n <button\n mat-icon-button\n *ngIf=\"canEdit\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n\n <!-- duplicate -->\n <button\n mat-icon-button\n *ngIf=\"canEdit && selection.selected | isArrayLength: { equals: 1 }\"\n [title]=\"'COMMON.BTN_DUPLICATE' | translate\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n <ion-refresher slot=\"fixed\" *ngIf=\"mobile\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n\n <!-- error -->\n <ion-item *ngIf=\"mobile && error\" lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\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 <button\n mat-icon-button\n color=\"accent\"\n *ngIf=\"filterPanelFloating\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"(filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate\"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '&#xbb;' : '&#xab;' }}</span>\n </mat-icon>\n </button>\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text translate>COMMON.BTN_CLOSE</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 translate>COMMON.BTN_APPLY</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 <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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-form-field *ngIf=\"!readOnly && row.id === -1 && row.editing; else readOnlyId\">\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('alreadyExists')\" translate>\n ERROR.FIELD_NOT_UNIQUE_ID\n </mat-error>\n </mat-form-field>\n\n <ng-template #readOnlyId>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n </ng-template>\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 [title]=\"'TABLE.TESTING.NAME' | translate\">{{ 'TABLE.TESTING.NAME' | translate }}</ion-label>\n <ion-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn = 'name'\">\n <mat-form-field *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\">\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 <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('maxlength')\" translate>\n ERROR.FIELD_MAX_LENGTH_COMPACT\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('minlength')\" translate>\n ERROR.FIELD_MIN_LENGTH_COMPACT\n </mat-error>\n </mat-form-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n </ng-template>\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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-autocomplete-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <mat-error matError *ngIf=\"(row.validator | formGetControl: 'levelId').hasError('invalid')\" translate>\n ERROR.FIELD_INVALID\n </mat-error>\n </mat-autocomplete-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\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 </ng-template>\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\">\n <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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\">\n <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <span *ngIf=\"row.editing && !row.validator.dirty\">-</span>\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 <ng-container *ngIf=\"loadingSubject | async; else noResult\">\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n </ng-container>\n\n <ng-template #noResult>\n <ion-item *ngIf=\"totalRowCount === 0\">\n <ion-text color=\"danger\" class=\"text-italic\" translate>COMMON.NO_RESULT</ion-text>\n </ion-item>\n </ng-template>\n\n <ion-infinite-scroll\n *ngIf=\"enableInfiniteScroll\"\n [threshold]=\"mobile ? '10%' : '2%'\"\n position=\"bottom\"\n (ionInfinite)=\"fetchMore($event)\"\n >\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n <mat-paginator\n *ngIf=\"!enableInfiniteScroll\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n\n <app-form-buttons-bar\n *ngIf=\"canEdit && !mobile\"\n (onCancel)=\"load()\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) || !dirty\"\n >\n <!-- error -->\n <ion-item *ngIf=\"error$ | async\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </app-form-buttons-bar>\n</ion-footer>\n\n<ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\" *ngIf=\"canEdit && mobile\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n</ion-fab>\n", styles: [".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"] }]
50784
+ // Provide options :
50785
+ // {
50786
+ // provide: APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN,
50787
+ // useFactory: () => {
50788
+ // return <CellSelectionServiceConfig<ICellId>>{
50789
+ // equals: (cell1, cell2) => {
50790
+ // console.debug('[table2-test] Comparing cell ids:', cell1, cell2);
50791
+ // return cell1?.rowId === cell2?.rowId && cell1?.columnName === cell2?.columnName;
50792
+ // },
50793
+ // };
50794
+ // },
50795
+ // },
50796
+ {
50797
+ provide: APP_CELL_SELECTION_SERVICE_TOKEN,
50798
+ useClass: CellSelectionService,
50799
+ },
50800
+ ], changeDetection: ChangeDetectionStrategy.OnPush, 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 <button mat-icon-button *ngIf=\"canEdit && !mobile\" [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"addRow()\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- reset filter -->\n <button mat-icon-button (click)=\"resetFilter()\" *ngIf=\"filterCriteriaCount\">\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 <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n <mat-icon\n *ngIf=\"filterCriteriaCount; else emptyFilter\"\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 <ng-template #emptyFilter>\n <mat-icon>filter_list_alt</mat-icon>\n </ng-template>\n </button>\n\n <!-- save -->\n <button mat-icon-button *ngIf=\"mobile\" [disabled]=\"(dirtySubject | async) !== true\" (click)=\"save()\">\n <mat-icon>save</mat-icon>\n </button>\n } @else {\n <!-- if row selection -->\n <!-- delete -->\n <button\n mat-icon-button\n *ngIf=\"canEdit\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"deleteSelection($event)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n\n <!-- duplicate -->\n <button\n mat-icon-button\n *ngIf=\"canEdit && selection.selected | isArrayLength: { equals: 1 }\"\n [title]=\"'COMMON.BTN_DUPLICATE' | translate\"\n (click)=\"duplicateRow($event, selection.selected[0])\"\n >\n <mat-icon>file_copy</mat-icon>\n </button>\n }\n </ion-buttons>\n</app-toolbar>\n<ion-content class=\"ion-no-padding\">\n <ion-refresher slot=\"fixed\" *ngIf=\"mobile\" (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n\n <!-- error -->\n <ion-item *ngIf=\"mobile && error\" lines=\"none\" @slideUpDownAnimation>\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\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 <button\n mat-icon-button\n color=\"accent\"\n *ngIf=\"filterPanelFloating\"\n (click)=\"toggleFilterPanelFloating()\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"(filterPanelFloating ? 'COMMON.BTN_EXPAND' : 'COMMON.BTN_HIDE') | translate\"\n >\n <mat-icon>\n <span style=\"transform: rotate(90deg)\">{{ filterPanelFloating ? '&#xbb;' : '&#xab;' }}</span>\n </mat-icon>\n </button>\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\" (click)=\"closeFilterPanel()\" [disabled]=\"loadingSubject | async\">\n <ion-text translate>COMMON.BTN_CLOSE</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 translate>COMMON.BTN_APPLY</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 (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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-form-field *ngIf=\"!readOnly && row.id === -1 && row.editing; else readOnlyId\">\n <input\n matInput\n autocomplete=\"off\"\n required\n [formControl]=\"row.validator | formGetControl: 'id'\"\n placeholder=\"Id\"\n [appAutofocus]=\"true\"\n />\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'id').hasError('alreadyExists')\" translate>\n ERROR.FIELD_NOT_UNIQUE_ID\n </mat-error>\n </mat-form-field>\n\n <ng-template #readOnlyId>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'id') || (row.currentData | propertyGet: 'id') }}\n </ion-label>\n </ng-template>\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 [title]=\"'TABLE.TESTING.NAME' | translate\">{{ 'TABLE.TESTING.NAME' | translate }}</ion-label>\n <ion-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\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 <mat-form-field *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\">\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 <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('required')\" translate>\n ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('maxlength')\" translate>\n ERROR.FIELD_MAX_LENGTH_COMPACT\n </mat-error>\n <mat-error *ngIf=\"(row.validator | formGetControl: 'name').hasError('minlength')\" translate>\n ERROR.FIELD_MIN_LENGTH_COMPACT\n </mat-error>\n </mat-form-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{ (row.validator | formGetValue: 'name') || (row.currentData | propertyGet: 'name') }}\n </ion-label>\n </ng-template>\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-label color=\"danger\" [innerHTML]=\"'&nbsp;*'\"></ion-label>\n </span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [appCellId]=\"{ rowId: row.id, columnName: 'levelId' }\">\n <mat-autocomplete-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <mat-error matError *ngIf=\"(row.validator | formGetControl: 'levelId').hasError('invalid')\" translate>\n ERROR.FIELD_INVALID\n </mat-error>\n </mat-autocomplete-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\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 </ng-template>\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 <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean') || (row.currentData | propertyGet: 'boolean')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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\">\n <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\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 <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <mat-boolean-field\n *ngIf=\"!readOnly && row.editing; else readOnlyTemplate\"\n floatLabel=\"never\"\n [style]=\"'button'\"\n [formControl]=\"\n (row.validator | formGetControl: 'boolean2') || formBuilder.control(row.currentData['boolean2'])\n \"\n ></mat-boolean-field>\n <ng-template #readOnlyTemplate>\n <ion-label appAutoTitle>\n {{\n (row.validator | formGetValue: 'boolean2') || (row.currentData | propertyGet: 'boolean2')\n ? ('COMMON.YES' | translate)\n : ('COMMON.NO' | translate)\n }}\n </ion-label>\n </ng-template>\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 <span *ngIf=\"row.editing && !row.validator.dirty\">-</span>\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 <ng-container *ngIf=\"loadingSubject | async; else noResult\">\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n </ng-container>\n\n <ng-template #noResult>\n <ion-item *ngIf=\"totalRowCount === 0\">\n <ion-text color=\"danger\" class=\"text-italic\" translate>COMMON.NO_RESULT</ion-text>\n </ion-item>\n </ng-template>\n\n <ion-infinite-scroll\n *ngIf=\"enableInfiniteScroll\"\n [threshold]=\"mobile ? '10%' : '2%'\"\n position=\"bottom\"\n (ionInfinite)=\"fetchMore($event)\"\n >\n <ion-infinite-scroll-content\n loadingSpinner=\"circles\"\n [loadingText]=\"'COMMON.LOADING_DOTS' | translate\"\n ></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n </div>\n</ion-content>\n\n<ion-footer>\n <!-- Paginator -->\n <mat-paginator\n *ngIf=\"!enableInfiniteScroll\"\n [length]=\"totalRowCount\"\n [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\"\n showFirstLastButtons\n ></mat-paginator>\n\n <app-form-buttons-bar\n *ngIf=\"canEdit && !mobile\"\n (onCancel)=\"load()\"\n (onSave)=\"save()\"\n [disabled]=\"(loadingSubject | async) || !dirty\"\n >\n <!-- error -->\n <ion-item *ngIf=\"errorSubject | async\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </app-form-buttons-bar>\n</ion-footer>\n\n<ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\" *ngIf=\"canEdit && mobile\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow($event)\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n</ion-fab>\n", styles: [".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"] }]
50344
50801
  }], ctorParameters: () => [{ type: i0.Injector }, { type: AppValidatorService }, { type: i1$3.UntypedFormBuilder }], propDecorators: { filterPanelFloating: [{
50345
50802
  type: Input
50346
50803
  }], sticky: [{
@@ -50629,13 +51086,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
50629
51086
 
50630
51087
  class TableTestingModule {
50631
51088
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TableTestingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
50632
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: TableTestingModule, declarations: [TableTestPage, Table2TestPage], imports: [CommonModule, SharedModule, CoreModule, i1$1.TranslateModule, FormsModule, ResizableModule], exports: [TableTestPage, Table2TestPage, TranslateModule] });
50633
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TableTestingModule, imports: [CommonModule, SharedModule, CoreModule, TranslateModule.forChild(), FormsModule, ResizableModule, TranslateModule] });
51089
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: TableTestingModule, declarations: [TableTestPage, Table2TestPage], imports: [CommonModule,
51090
+ SharedModule,
51091
+ CoreModule, i1$1.TranslateModule, FormsModule,
51092
+ ResizableModule,
51093
+ CellIdentifierDirective,
51094
+ CellSelectionDirective], exports: [TableTestPage, Table2TestPage, TranslateModule] });
51095
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TableTestingModule, imports: [CommonModule,
51096
+ SharedModule,
51097
+ CoreModule,
51098
+ TranslateModule.forChild(),
51099
+ FormsModule,
51100
+ ResizableModule, TranslateModule] });
50634
51101
  }
50635
51102
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TableTestingModule, decorators: [{
50636
51103
  type: NgModule,
50637
51104
  args: [{
50638
- imports: [CommonModule, SharedModule, CoreModule, TranslateModule.forChild(), FormsModule, ResizableModule],
51105
+ imports: [
51106
+ CommonModule,
51107
+ SharedModule,
51108
+ CoreModule,
51109
+ TranslateModule.forChild(),
51110
+ FormsModule,
51111
+ ResizableModule,
51112
+ CellIdentifierDirective,
51113
+ CellSelectionDirective,
51114
+ ],
50639
51115
  declarations: [TableTestPage, Table2TestPage],
50640
51116
  exports: [TableTestPage, Table2TestPage, TranslateModule],
50641
51117
  }]
@@ -51786,5 +52262,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
51786
52262
  * Generated bundle index. Do not edit.
51787
52263
  */
51788
52264
 
51789
- export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOTKEYS_CONFIG, 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_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdditionalFields, 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, AppGraphQLModule, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, 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, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, 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, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileResponse, FileService, FileSizePipe, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, 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, 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, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NavActionsColumnComponent, 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, 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_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, UsersUtils, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, decorateWithTakeUntil, 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, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hexToRgb, hexToRgbArray, initArrayControlsFromValues, interpolateString, intersectArrays, isAndroid, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isMacOS, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isTouchUi, isVersionCompatible, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, 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, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
52265
+ export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOTKEYS_CONFIG, 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_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdditionalFields, 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, AppGraphQLModule, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, 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, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, 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, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileResponse, FileService, FileSizePipe, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, 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, 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, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NavActionsColumnComponent, 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, 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_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, UsersUtils, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, decorateWithTakeUntil, 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, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hexToRgb, hexToRgbArray, initArrayControlsFromValues, interpolateString, intersectArrays, isAndroid, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isMacOS, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isTouchUi, isVersionCompatible, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, 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, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
51790
52266
  //# sourceMappingURL=sumaris-net.ngx-components.mjs.map