@fundamental-ngx/platform 0.43.36 → 0.43.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/form/multi-combobox/multi-combobox/multi-combobox.component.mjs +3 -1
- package/esm2020/table/components/table-row/table-row.component.mjs +4 -3
- package/esm2020/table/table.component.mjs +43 -10
- package/esm2020/table-helpers/directives/table-virtual-scroll.directive.mjs +235 -37
- package/esm2020/table-helpers/models/directives.mjs +1 -1
- package/esm2020/table-helpers/services/table-column-resize.service.mjs +26 -5
- package/esm2020/table-helpers/table.mjs +1 -1
- package/fesm2015/fundamental-ngx-platform-form.mjs +2 -0
- package/fesm2015/fundamental-ngx-platform-form.mjs.map +1 -1
- package/fesm2015/fundamental-ngx-platform-table-helpers.mjs +363 -147
- package/fesm2015/fundamental-ngx-platform-table-helpers.mjs.map +1 -1
- package/fesm2015/fundamental-ngx-platform-table.mjs +165 -131
- package/fesm2015/fundamental-ngx-platform-table.mjs.map +1 -1
- package/fesm2020/fundamental-ngx-platform-form.mjs +2 -0
- package/fesm2020/fundamental-ngx-platform-form.mjs.map +1 -1
- package/fesm2020/fundamental-ngx-platform-table-helpers.mjs +364 -148
- package/fesm2020/fundamental-ngx-platform-table-helpers.mjs.map +1 -1
- package/fesm2020/fundamental-ngx-platform-table.mjs +164 -131
- package/fesm2020/fundamental-ngx-platform-table.mjs.map +1 -1
- package/fundamental-ngx-platform-v0.43.37.tgz +0 -0
- package/package.json +4 -4
- package/schematics/ng-add/index.js +1 -1
- package/table/table.component.d.ts +9 -0
- package/table-helpers/directives/table-virtual-scroll.directive.d.ts +64 -5
- package/table-helpers/models/directives.d.ts +1 -0
- package/table-helpers/services/table-column-resize.service.d.ts +4 -0
- package/table-helpers/table.d.ts +10 -2
- package/fundamental-ngx-platform-v0.43.36.tgz +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, EventEmitter, inject, Directive, Input, Output, NgZone, ChangeDetectorRef, forwardRef, HostBinding, Optional, ElementRef, Inject, Pipe, NgModule } from '@angular/core';
|
|
2
|
+
import { InjectionToken, Injectable, EventEmitter, inject, Directive, Input, Output, NgZone, ChangeDetectorRef, forwardRef, HostBinding, HostListener, Optional, ElementRef, Inject, Pipe, NgModule } from '@angular/core';
|
|
3
3
|
import * as i2 from '@fundamental-ngx/cdk/utils';
|
|
4
|
-
import { DestroyedService, isString, dfs,
|
|
4
|
+
import { DestroyedService, isString, dfs, KeyUtil, RtlService, resizeObservable, FDK_FOCUSABLE_ITEM_DIRECTIVE, FocusableItemDirective } from '@fundamental-ngx/cdk/utils';
|
|
5
5
|
import { isDataSource, DataSourceDirective, FD_DATA_SOURCE_TRANSFORMER } from '@fundamental-ngx/cdk/data-source';
|
|
6
6
|
import { Subject, BehaviorSubject, isObservable, Subscription, filter as filter$1, fromEvent, Observable, take as take$1, takeUntil as takeUntil$1 } from 'rxjs';
|
|
7
7
|
import { map, takeUntil, take, skip, startWith, filter, debounceTime, distinctUntilChanged, share, tap } from 'rxjs/operators';
|
|
@@ -12,7 +12,8 @@ import { get } from 'lodash-es';
|
|
|
12
12
|
import equal from 'fast-deep-equal';
|
|
13
13
|
import cloneDeep from 'lodash-es/cloneDeep';
|
|
14
14
|
import set from 'lodash-es/set';
|
|
15
|
-
import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
|
|
15
|
+
import { UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
|
|
16
|
+
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
16
17
|
import { DOCUMENT } from '@angular/common';
|
|
17
18
|
import { TableCellDirective } from '@fundamental-ngx/core/table';
|
|
18
19
|
import * as i1 from '@angular/forms';
|
|
@@ -2319,6 +2320,113 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
|
|
|
2319
2320
|
type: Injectable
|
|
2320
2321
|
}] });
|
|
2321
2322
|
|
|
2323
|
+
class TableRowService {
|
|
2324
|
+
constructor() {
|
|
2325
|
+
/** @hidden */
|
|
2326
|
+
this._toggleRowSubject = new Subject();
|
|
2327
|
+
/** @hidden */
|
|
2328
|
+
this._scrollToOverlappedCellSubject = new Subject();
|
|
2329
|
+
/** @hidden */
|
|
2330
|
+
this._cellClickedSubject = new Subject();
|
|
2331
|
+
/** @hidden */
|
|
2332
|
+
this._cellFocusedSubject = new Subject();
|
|
2333
|
+
/** @hidden */
|
|
2334
|
+
this._cellActivateSubject = new Subject();
|
|
2335
|
+
/** Stream that emits when the table cell being focused. */
|
|
2336
|
+
this.cellActivate$ = this._cellActivateSubject.asObservable();
|
|
2337
|
+
/** @hidden */
|
|
2338
|
+
this._toggleAllSelectableRowsSubject = new Subject();
|
|
2339
|
+
/** Stream that emits when toggling all selectable rows is needed. */
|
|
2340
|
+
this.toggleAllSelectableRows$ = this._toggleAllSelectableRowsSubject.asObservable();
|
|
2341
|
+
/** Stream that emits when table cell being clicked. */
|
|
2342
|
+
this.cellClicked$ = this._cellClickedSubject.asObservable();
|
|
2343
|
+
/** Stream that emits when scrolling to overlapped cell is needed. */
|
|
2344
|
+
this.scrollToOverlappedCell$ = this._scrollToOverlappedCellSubject.asObservable();
|
|
2345
|
+
/** Stream that emits when the table cell being focused. */
|
|
2346
|
+
this.cellFocused$ = this._cellFocusedSubject.asObservable();
|
|
2347
|
+
/** Toggle row stream. */
|
|
2348
|
+
this.toggleRow$ = this._toggleRowSubject.asObservable();
|
|
2349
|
+
/** Editable cells map. */
|
|
2350
|
+
this.editableCells = new Map();
|
|
2351
|
+
/** @hidden */
|
|
2352
|
+
this.childRowsAdded$ = new Subject();
|
|
2353
|
+
/** Stream to load child items for a particular rows. */
|
|
2354
|
+
this.loadChildRows$ = new Subject();
|
|
2355
|
+
}
|
|
2356
|
+
/** `toggleRow$` stream trigger. */
|
|
2357
|
+
toggleRow(evt) {
|
|
2358
|
+
this._toggleRowSubject.next(evt);
|
|
2359
|
+
}
|
|
2360
|
+
/** `scrollToOverlappedCell$` stream trigger. */
|
|
2361
|
+
scrollToOverlappedCell() {
|
|
2362
|
+
this._scrollToOverlappedCellSubject.next();
|
|
2363
|
+
}
|
|
2364
|
+
/** `cellClicked$` stream trigger. */
|
|
2365
|
+
cellClicked(evt) {
|
|
2366
|
+
this._cellClickedSubject.next(evt);
|
|
2367
|
+
}
|
|
2368
|
+
/** @hidden */
|
|
2369
|
+
cellActivate(columnIndex, row) {
|
|
2370
|
+
this._cellActivateSubject.next({ columnIndex, row: row.value });
|
|
2371
|
+
}
|
|
2372
|
+
/** `cellFocused$` stream trigger. */
|
|
2373
|
+
cellFocused(evt) {
|
|
2374
|
+
this._cellFocusedSubject.next(evt);
|
|
2375
|
+
}
|
|
2376
|
+
/** `toggleAllSelectableRows$` stream trigger. */
|
|
2377
|
+
toggleAllSelectableRows(selectAll) {
|
|
2378
|
+
this._toggleAllSelectableRowsSubject.next(selectAll);
|
|
2379
|
+
}
|
|
2380
|
+
/** Groups the table rows. */
|
|
2381
|
+
groupTableRows(sourceRows, groups, groupRulesMap) {
|
|
2382
|
+
const groupRules = Array.from(groups);
|
|
2383
|
+
if (!groupRules.length) {
|
|
2384
|
+
/**
|
|
2385
|
+
* In case if previously we had groups with collapsed items
|
|
2386
|
+
* but now we don't have it we need to reset row.hidden flag
|
|
2387
|
+
* in order to avoid empty table after groups settings removing
|
|
2388
|
+
*/
|
|
2389
|
+
sourceRows.forEach((row) => {
|
|
2390
|
+
row.hidden = false;
|
|
2391
|
+
});
|
|
2392
|
+
return sourceRows;
|
|
2393
|
+
}
|
|
2394
|
+
// Build tree like groups
|
|
2395
|
+
const treeLikeGroupedRows = createGroupedTableRowsTree(groupRules, sourceRows);
|
|
2396
|
+
// Sort tree like groups
|
|
2397
|
+
const sortedTreeLikeGroupedRows = sortTreeLikeGroupedRows(treeLikeGroupedRows, groupRulesMap);
|
|
2398
|
+
// Convert tree like list to a flat list
|
|
2399
|
+
return convertTreeLikeToFlatList(sortedTreeLikeGroupedRows);
|
|
2400
|
+
}
|
|
2401
|
+
/** Sets editable cells for particular row. */
|
|
2402
|
+
updateEditableCells(row, cells) {
|
|
2403
|
+
this.editableCells.set(row, cells);
|
|
2404
|
+
}
|
|
2405
|
+
/** Removes editable cells of particular row. */
|
|
2406
|
+
removeEditableCells(row) {
|
|
2407
|
+
this.editableCells.delete(row);
|
|
2408
|
+
}
|
|
2409
|
+
/** Triggers stream to load child items for a particular rows. */
|
|
2410
|
+
loadChildRows(rows) {
|
|
2411
|
+
const expandableRows = [];
|
|
2412
|
+
if (Array.isArray(rows)) {
|
|
2413
|
+
expandableRows.push(...rows.filter((r) => !r.childItemsLoading$.value));
|
|
2414
|
+
}
|
|
2415
|
+
else {
|
|
2416
|
+
if (rows.childItemsLoading$.value) {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
expandableRows.push(rows);
|
|
2420
|
+
}
|
|
2421
|
+
this.loadChildRows$.next(expandableRows);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
TableRowService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableRowService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2425
|
+
TableRowService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableRowService });
|
|
2426
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableRowService, decorators: [{
|
|
2427
|
+
type: Injectable
|
|
2428
|
+
}] });
|
|
2429
|
+
|
|
2322
2430
|
class TableVirtualScrollDirective extends TableVirtualScroll {
|
|
2323
2431
|
constructor() {
|
|
2324
2432
|
super(...arguments);
|
|
@@ -2342,7 +2450,103 @@ class TableVirtualScrollDirective extends TableVirtualScroll {
|
|
|
2342
2450
|
/** @hidden */
|
|
2343
2451
|
this._tableScrollDispatcher = inject(TableScrollDispatcherService);
|
|
2344
2452
|
/** @hidden */
|
|
2453
|
+
this._tableRowService = inject(TableRowService);
|
|
2454
|
+
/** @hidden */
|
|
2345
2455
|
this._destroy$ = inject(DestroyedService);
|
|
2456
|
+
/** @hidden */
|
|
2457
|
+
this._lastMockScrollPosition = 0;
|
|
2458
|
+
/** @hidden */
|
|
2459
|
+
this._previousStartNodeIndex = null;
|
|
2460
|
+
/** @hidden */
|
|
2461
|
+
this._scrollWholeRows = false;
|
|
2462
|
+
/**
|
|
2463
|
+
* @hidden
|
|
2464
|
+
* This function is called when the user performs a wheel event. It determines whether or not the user has scrolled
|
|
2465
|
+
* up or down using the event's deltaX property, and scrolls the table by one row.
|
|
2466
|
+
*/
|
|
2467
|
+
this._wheelScrollListenerFunction = (event) => {
|
|
2468
|
+
if (Math.abs(event.deltaX) < Math.abs(event.deltaY)) {
|
|
2469
|
+
event.preventDefault();
|
|
2470
|
+
event.stopImmediatePropagation();
|
|
2471
|
+
if (this._wheelTimeout) {
|
|
2472
|
+
window.cancelAnimationFrame(this._wheelTimeout);
|
|
2473
|
+
}
|
|
2474
|
+
const deltaY = event.deltaY;
|
|
2475
|
+
if (deltaY) {
|
|
2476
|
+
this._wheelTimeout = window.requestAnimationFrame(() => {
|
|
2477
|
+
this._scrollRow(deltaY > 0 ? 1 : -1);
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
/**
|
|
2483
|
+
* @hidden
|
|
2484
|
+
* This function is called when the mock scrollbar (when using 'scrollWholeRows') is moved. It determines if the
|
|
2485
|
+
* scroll was up or down by comparing the current scroll top to the previous scroll top and scrolls rows accordingly.
|
|
2486
|
+
*/
|
|
2487
|
+
this._mockScrollbarListenerFunction = (event) => {
|
|
2488
|
+
event.preventDefault();
|
|
2489
|
+
event.stopImmediatePropagation();
|
|
2490
|
+
if (this._scrollMockTimeout) {
|
|
2491
|
+
window.cancelAnimationFrame(this._scrollMockTimeout);
|
|
2492
|
+
}
|
|
2493
|
+
const tableScrollMockContainer = this._table.tableScrollMockContainer.nativeElement;
|
|
2494
|
+
let deltaY = tableScrollMockContainer.scrollTop - this._lastMockScrollPosition;
|
|
2495
|
+
deltaY = Math.ceil(deltaY / this.rowHeight);
|
|
2496
|
+
this._scrollMockTimeout = window.requestAnimationFrame(() => {
|
|
2497
|
+
this._scrollRow(deltaY, true);
|
|
2498
|
+
this._lastMockScrollPosition = tableScrollMockContainer.scrollTop;
|
|
2499
|
+
if (tableScrollMockContainer.scrollTop === 0) {
|
|
2500
|
+
// Always display the first row of the table when the user scrolls all the way to the top
|
|
2501
|
+
this._setRows(0);
|
|
2502
|
+
}
|
|
2503
|
+
if (tableScrollMockContainer.scrollTop + tableScrollMockContainer.getBoundingClientRect().height ===
|
|
2504
|
+
tableScrollMockContainer.scrollHeight) {
|
|
2505
|
+
// Always display the last rows of the table when the user scrolls all the way to the bottom
|
|
2506
|
+
const startingNodeIndex = this._table._tableRowsVisible.length - this._getNumberOfRowsToDisplay();
|
|
2507
|
+
this._setRows(startingNodeIndex);
|
|
2508
|
+
}
|
|
2509
|
+
});
|
|
2510
|
+
};
|
|
2511
|
+
}
|
|
2512
|
+
/**
|
|
2513
|
+
* Whether to scroll whole rows rather than pixel by pixel when the user performs a scroll.
|
|
2514
|
+
* This gives greater performance improvements but also lends to some potentially undesirable UX changes.
|
|
2515
|
+
* A "rowHeight" must be provided when using this feature.
|
|
2516
|
+
*/
|
|
2517
|
+
set scrollWholeRows(value) {
|
|
2518
|
+
this._scrollWholeRows = coerceBooleanProperty(value);
|
|
2519
|
+
if (this._scrollWholeRows) {
|
|
2520
|
+
this.virtualScroll = true;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
get scrollWholeRows() {
|
|
2524
|
+
return this._scrollWholeRows;
|
|
2525
|
+
}
|
|
2526
|
+
/** @hidden */
|
|
2527
|
+
_keydownHandler(event) {
|
|
2528
|
+
if (this.scrollWholeRows) {
|
|
2529
|
+
if (this._focusedCell?.rowIndex === 0 && KeyUtil.isKeyCode(event, UP_ARROW)) {
|
|
2530
|
+
if (this._table.tableContainer.nativeElement.querySelectorAll('tr')[1].ariaRowIndex !== '0') {
|
|
2531
|
+
const cellToFocus = {
|
|
2532
|
+
colIndex: this._focusedCell.colIndex,
|
|
2533
|
+
rowIndex: 1
|
|
2534
|
+
};
|
|
2535
|
+
this._table._focusableGrid.focusCell(cellToFocus);
|
|
2536
|
+
}
|
|
2537
|
+
this._scrollRow(-1);
|
|
2538
|
+
}
|
|
2539
|
+
else if (this._focusedCell?.rowIndex === this._getNumberOfRowsToDisplay() &&
|
|
2540
|
+
KeyUtil.isKeyCode(event, DOWN_ARROW)) {
|
|
2541
|
+
this._scrollRow(1);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
/** @hidden */
|
|
2546
|
+
ngOnInit() {
|
|
2547
|
+
if (this.scrollWholeRows && !this.virtualScroll) {
|
|
2548
|
+
this.virtualScroll = true;
|
|
2549
|
+
}
|
|
2346
2550
|
}
|
|
2347
2551
|
/** @hidden */
|
|
2348
2552
|
ngOnChanges(changes) {
|
|
@@ -2353,58 +2557,152 @@ class TableVirtualScrollDirective extends TableVirtualScroll {
|
|
|
2353
2557
|
this.calculateVirtualScrollRows();
|
|
2354
2558
|
}
|
|
2355
2559
|
}
|
|
2560
|
+
/** @hidden */
|
|
2561
|
+
ngOnDestroy() {
|
|
2562
|
+
this._focusableGridSubscription?.unsubscribe();
|
|
2563
|
+
}
|
|
2356
2564
|
/** Sets table reference. */
|
|
2357
2565
|
setTable(table) {
|
|
2358
2566
|
this._table = table;
|
|
2359
2567
|
}
|
|
2360
2568
|
/**
|
|
2361
|
-
*
|
|
2569
|
+
* This function determines how many rows can be displayed in the table, and sets the rows in the table based on the
|
|
2570
|
+
* start node index, which may come from the table state
|
|
2362
2571
|
*/
|
|
2363
2572
|
calculateVirtualScrollRows() {
|
|
2364
|
-
if (
|
|
2365
|
-
|
|
2573
|
+
if (this.scrollWholeRows) {
|
|
2574
|
+
if (!this._focusableGridSubscription) {
|
|
2575
|
+
this._focusableGridSubscription = this._table._focusableGrid.itemFocused.subscribe((event) => {
|
|
2576
|
+
this._focusedCell = event;
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
if (!this.virtualScroll || !this.bodyHeight) {
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
let startNodeIndex = 0;
|
|
2583
|
+
const scrollTop = this._table.getTableState().scrollTopPosition;
|
|
2584
|
+
if (scrollTop !== 0) {
|
|
2585
|
+
startNodeIndex = Math.floor(scrollTop / this.rowHeight);
|
|
2586
|
+
if (startNodeIndex > this._table._tableRowsVisible.length) {
|
|
2587
|
+
startNodeIndex = 0;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
const totalNodeCount = this._table._tableRowsVisible.length;
|
|
2591
|
+
let visibleNodeCount = Math.floor(this._table.tableContainer.nativeElement.clientHeight / this.rowHeight);
|
|
2592
|
+
visibleNodeCount = Math.min(totalNodeCount - startNodeIndex, visibleNodeCount);
|
|
2593
|
+
this.virtualScrollTotalHeight = totalNodeCount * this.rowHeight - visibleNodeCount * this.rowHeight;
|
|
2594
|
+
this._setRows(startNodeIndex);
|
|
2595
|
+
const scrollMockContainer = this._table.tableScrollMockContainer.nativeElement;
|
|
2596
|
+
scrollMockContainer.style.maxHeight = this.bodyHeight;
|
|
2366
2597
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
visibleNodeCount
|
|
2380
|
-
|
|
2381
|
-
//
|
|
2382
|
-
startNodeIndex ===
|
|
2383
|
-
|
|
2384
|
-
|
|
2598
|
+
else {
|
|
2599
|
+
if (!this.virtualScroll || !this.bodyHeight) {
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
const rowHeight = this.rowHeight + 1;
|
|
2603
|
+
const rowsVisible = this._table._tableRowsVisible;
|
|
2604
|
+
const rowsInViewPort = this._table.getRowsInViewport();
|
|
2605
|
+
const totalNodeCount = rowsVisible.length;
|
|
2606
|
+
const scrollTop = this._table.tableScrollable.getScrollTop();
|
|
2607
|
+
let startNodeIndex = Math.floor(scrollTop / rowHeight) - this.renderAhead;
|
|
2608
|
+
startNodeIndex = Math.max(0, startNodeIndex);
|
|
2609
|
+
let visibleNodeCount = Math.ceil(this._table.tableContainer.nativeElement.clientHeight / rowHeight) + 2 * this.renderAhead;
|
|
2610
|
+
visibleNodeCount = Math.min(totalNodeCount - startNodeIndex, visibleNodeCount);
|
|
2611
|
+
this.virtualScrollTransform$.next(startNodeIndex * rowHeight);
|
|
2612
|
+
// Simple caching to avoid unnecessary re-renderings
|
|
2613
|
+
const isCached = startNodeIndex === this._virtualScrollCache.startNodeIndex &&
|
|
2614
|
+
visibleNodeCount === this._virtualScrollCache.visibleNodeCount &&
|
|
2615
|
+
totalNodeCount === this._virtualScrollCache.totalNodeCount &&
|
|
2616
|
+
// On rows change, even if the total number of rows is the same, the row object will be different
|
|
2617
|
+
startNodeIndex === rowsInViewPort[0];
|
|
2618
|
+
if (isCached) {
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
this._virtualScrollCache = { startNodeIndex, visibleNodeCount, totalNodeCount };
|
|
2622
|
+
this.virtualScrollTotalHeight = totalNodeCount * rowHeight - visibleNodeCount * rowHeight;
|
|
2623
|
+
this._table.setRowsInViewport(startNodeIndex, rowsVisible.slice(startNodeIndex, startNodeIndex + visibleNodeCount).length);
|
|
2385
2624
|
}
|
|
2386
|
-
this._virtualScrollCache = { startNodeIndex, visibleNodeCount, totalNodeCount };
|
|
2387
|
-
this.virtualScrollTotalHeight = totalNodeCount * rowHeight - visibleNodeCount * rowHeight;
|
|
2388
|
-
this._table.setRowsInViewport(startNodeIndex, rowsVisible.slice(startNodeIndex, startNodeIndex + visibleNodeCount).length);
|
|
2389
2625
|
}
|
|
2390
2626
|
/**
|
|
2391
|
-
*
|
|
2627
|
+
* @hidden
|
|
2628
|
+
* Initialises scroll listeners.
|
|
2392
2629
|
*/
|
|
2393
2630
|
listenOnVirtualScroll() {
|
|
2394
|
-
this.
|
|
2395
|
-
.
|
|
2396
|
-
|
|
2397
|
-
.subscribe((scrollable) => {
|
|
2398
|
-
this.calculateVirtualScrollRows();
|
|
2399
|
-
this._table.setTableState({
|
|
2400
|
-
...this._table.getTableState(),
|
|
2401
|
-
scrollTopPosition: scrollable.getScrollTop()
|
|
2631
|
+
if (this.scrollWholeRows) {
|
|
2632
|
+
this._table.tableContainer.nativeElement.addEventListener('wheel', this._wheelScrollListenerFunction, {
|
|
2633
|
+
passive: false
|
|
2402
2634
|
});
|
|
2635
|
+
this._table.tableScrollMockContainer.nativeElement.addEventListener('scroll', this._mockScrollbarListenerFunction, { passive: false });
|
|
2636
|
+
}
|
|
2637
|
+
else {
|
|
2638
|
+
this._tableScrollDispatcher
|
|
2639
|
+
.verticallyScrolled()
|
|
2640
|
+
.pipe(filter$1(() => this.virtualScroll && !!this.bodyHeight), takeUntil(this._destroy$))
|
|
2641
|
+
.subscribe((scrollable) => {
|
|
2642
|
+
this.calculateVirtualScrollRows();
|
|
2643
|
+
this._table.setTableState({
|
|
2644
|
+
...this._table.getTableState(),
|
|
2645
|
+
scrollTopPosition: scrollable.getScrollTop()
|
|
2646
|
+
});
|
|
2647
|
+
});
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
/** @hidden */
|
|
2651
|
+
_getNumberOfRowsToDisplay() {
|
|
2652
|
+
const tableContainer = this._table.tableContainer.nativeElement;
|
|
2653
|
+
let tableHeight = tableContainer.clientHeight;
|
|
2654
|
+
tableHeight = tableHeight - tableContainer.querySelector('thead').clientHeight;
|
|
2655
|
+
return Math.floor(tableHeight / (this.rowHeight + 2));
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* @hidden
|
|
2659
|
+
* This function, used only when 'scrollWholeRows' is truthy, calculates what should be the new starting node, based
|
|
2660
|
+
* off the previous starting node and the provided 'count' parameter. For example if the previous start node was 10
|
|
2661
|
+
* and the count parameter is -2, the new starting node will be 8. This function also programmatically moves the
|
|
2662
|
+
* mock scrollbar.
|
|
2663
|
+
*/
|
|
2664
|
+
_scrollRow(count, fromMockScrollbarListener = false) {
|
|
2665
|
+
let startingNode = 0;
|
|
2666
|
+
if (this._previousStartNodeIndex !== null && this._previousStartNodeIndex >= 0) {
|
|
2667
|
+
startingNode = this._previousStartNodeIndex;
|
|
2668
|
+
}
|
|
2669
|
+
if (startingNode + count + this._getNumberOfRowsToDisplay() <= this._table._tableRowsVisible.length ||
|
|
2670
|
+
count < 0) {
|
|
2671
|
+
startingNode = Math.ceil(startingNode + count);
|
|
2672
|
+
}
|
|
2673
|
+
if (startingNode < 0) {
|
|
2674
|
+
startingNode = 0;
|
|
2675
|
+
}
|
|
2676
|
+
// This next 'if' statement is for triggering new page loads when 'pageScrolling' is true
|
|
2677
|
+
if (count > 0 &&
|
|
2678
|
+
startingNode === this._previousStartNodeIndex &&
|
|
2679
|
+
this._table.pageScrolling &&
|
|
2680
|
+
this._table._tableRowsVisible.length === this._table._tableRows.length) {
|
|
2681
|
+
this._table._onSpyIntersect(true);
|
|
2682
|
+
}
|
|
2683
|
+
this._setRows(startingNode);
|
|
2684
|
+
const tableScrollMockContainer = this._table.tableScrollMockContainer.nativeElement;
|
|
2685
|
+
if (!fromMockScrollbarListener) {
|
|
2686
|
+
tableScrollMockContainer.scrollBy({ top: count * this.rowHeight });
|
|
2687
|
+
}
|
|
2688
|
+
this._lastMockScrollPosition = tableScrollMockContainer.scrollTop;
|
|
2689
|
+
}
|
|
2690
|
+
/**
|
|
2691
|
+
* @hidden
|
|
2692
|
+
* This function sets rows in the table's viewport depending on the provided starting node. It also sets the table
|
|
2693
|
+
* state.
|
|
2694
|
+
*/
|
|
2695
|
+
_setRows(startingNode) {
|
|
2696
|
+
this._table.setRowsInViewport(startingNode, this._getNumberOfRowsToDisplay());
|
|
2697
|
+
this._previousStartNodeIndex = startingNode;
|
|
2698
|
+
this._table.setTableState({
|
|
2699
|
+
...this._table.getTableState(),
|
|
2700
|
+
scrollTopPosition: this.rowHeight * startingNode
|
|
2403
2701
|
});
|
|
2404
2702
|
}
|
|
2405
2703
|
}
|
|
2406
2704
|
TableVirtualScrollDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableVirtualScrollDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
2407
|
-
TableVirtualScrollDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TableVirtualScrollDirective, isStandalone: true, selector: "fdp-table[virtualScroll]", inputs: { virtualScroll: "virtualScroll", renderAhead: "renderAhead", bodyHeight: "bodyHeight", rowHeight: "rowHeight" }, providers: [
|
|
2705
|
+
TableVirtualScrollDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TableVirtualScrollDirective, isStandalone: true, selector: "fdp-table[virtualScroll]", inputs: { virtualScroll: "virtualScroll", scrollWholeRows: "scrollWholeRows", renderAhead: "renderAhead", bodyHeight: "bodyHeight", rowHeight: "rowHeight" }, host: { listeners: { "keydown": "_keydownHandler($event)" } }, providers: [
|
|
2408
2706
|
DestroyedService,
|
|
2409
2707
|
{
|
|
2410
2708
|
provide: FDP_TABLE_VIRTUAL_SCROLL_DIRECTIVE,
|
|
@@ -2427,12 +2725,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
|
|
|
2427
2725
|
}]
|
|
2428
2726
|
}], propDecorators: { virtualScroll: [{
|
|
2429
2727
|
type: Input
|
|
2728
|
+
}], scrollWholeRows: [{
|
|
2729
|
+
type: Input
|
|
2430
2730
|
}], renderAhead: [{
|
|
2431
2731
|
type: Input
|
|
2432
2732
|
}], bodyHeight: [{
|
|
2433
2733
|
type: Input
|
|
2434
2734
|
}], rowHeight: [{
|
|
2435
2735
|
type: Input
|
|
2736
|
+
}], _keydownHandler: [{
|
|
2737
|
+
type: HostListener,
|
|
2738
|
+
args: ['keydown', ['$event']]
|
|
2436
2739
|
}] } });
|
|
2437
2740
|
|
|
2438
2741
|
class TableInitialStateDirective extends TableInitialState {
|
|
@@ -2601,6 +2904,8 @@ class TableColumnResizeService {
|
|
|
2601
2904
|
this._destroyed = new Subject();
|
|
2602
2905
|
/** @hidden */
|
|
2603
2906
|
this._resizerMoveSubscription = new Subscription();
|
|
2907
|
+
/** @hidden */
|
|
2908
|
+
this._initialTableWidth = null;
|
|
2604
2909
|
/** Resize progress stream. */
|
|
2605
2910
|
this.resizeInProgress$ = new BehaviorSubject(this._resizeInProgress);
|
|
2606
2911
|
/** Whether cell mock should be visible. */
|
|
@@ -2665,8 +2970,24 @@ class TableColumnResizeService {
|
|
|
2665
2970
|
}
|
|
2666
2971
|
/** Retrieves custom column value or returns `unset` */
|
|
2667
2972
|
getColumnWidthStyle(columnName) {
|
|
2668
|
-
|
|
2669
|
-
|
|
2973
|
+
if (this._tableRef._virtualScrollDirective?.scrollWholeRows) {
|
|
2974
|
+
if (!this._initialTableWidth) {
|
|
2975
|
+
this._initialTableWidth = this._tableRef._tableWidthPx;
|
|
2976
|
+
}
|
|
2977
|
+
const selectionColumnWidth = SELECTION_COLUMN_WIDTH.get(this._tableRef.contentDensityObserver.value) ?? 0;
|
|
2978
|
+
let sizeDividedByColumnsCount = this._initialTableWidth / this._tableRef.getVisibleTableColumns().length -
|
|
2979
|
+
selectionColumnWidth / this._tableRef.getVisibleTableColumns().length;
|
|
2980
|
+
// In the event of tables with a very high number of columns, need to prevent the default behavior which
|
|
2981
|
+
// would make each column not wide enough to be usable and instead use a horizontal scrollbar.
|
|
2982
|
+
if (sizeDividedByColumnsCount < 176) {
|
|
2983
|
+
sizeDividedByColumnsCount = 176;
|
|
2984
|
+
}
|
|
2985
|
+
return this._fixedColumnsWidthMap.get(columnName) || sizeDividedByColumnsCount + 'px';
|
|
2986
|
+
}
|
|
2987
|
+
else {
|
|
2988
|
+
const calculatedWidth = this._fixedColumnsWidthMap.get(columnName);
|
|
2989
|
+
return calculatedWidth || 'unset';
|
|
2990
|
+
}
|
|
2670
2991
|
}
|
|
2671
2992
|
/** Previous column name */
|
|
2672
2993
|
getPreviousColumnName(columnName) {
|
|
@@ -2717,7 +3038,6 @@ class TableColumnResizeService {
|
|
|
2717
3038
|
this._resizerPosition = resizerPosition - TABLE_RESIZER_BORDER_WIDTH + scrollLeftOffset;
|
|
2718
3039
|
this.resizerPosition$.next(this.resizerPosition);
|
|
2719
3040
|
}
|
|
2720
|
-
this._markForCheck.next();
|
|
2721
3041
|
}
|
|
2722
3042
|
/** Hide the column resizer. */
|
|
2723
3043
|
hideResizer() {
|
|
@@ -2798,6 +3118,10 @@ class TableColumnResizeService {
|
|
|
2798
3118
|
this._updateHeaderOverflowState(updatedWidth - padding);
|
|
2799
3119
|
this._markForCheck.next();
|
|
2800
3120
|
}
|
|
3121
|
+
/** @hidden */
|
|
3122
|
+
_setInitialTableWidth() {
|
|
3123
|
+
this._initialTableWidth = this._tableRef._tableWidthPx;
|
|
3124
|
+
}
|
|
2801
3125
|
/** Update column resizer position. */
|
|
2802
3126
|
_updateResizerPositionOnMouseMove() {
|
|
2803
3127
|
this._resizerMoveSubscription = fromEvent(document, 'mousemove')
|
|
@@ -2807,7 +3131,6 @@ class TableColumnResizeService {
|
|
|
2807
3131
|
const diffX = this._rtl ? clientStartX - event.clientX : event.clientX - clientStartX;
|
|
2808
3132
|
this._resizerPosition = (this._startX ?? 0) + diffX;
|
|
2809
3133
|
this.resizerPosition$.next(this.resizerPosition);
|
|
2810
|
-
this._markForCheck.next();
|
|
2811
3134
|
});
|
|
2812
3135
|
}
|
|
2813
3136
|
/**
|
|
@@ -2836,113 +3159,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
|
|
|
2836
3159
|
type: Optional
|
|
2837
3160
|
}] }]; } });
|
|
2838
3161
|
|
|
2839
|
-
class TableRowService {
|
|
2840
|
-
constructor() {
|
|
2841
|
-
/** @hidden */
|
|
2842
|
-
this._toggleRowSubject = new Subject();
|
|
2843
|
-
/** @hidden */
|
|
2844
|
-
this._scrollToOverlappedCellSubject = new Subject();
|
|
2845
|
-
/** @hidden */
|
|
2846
|
-
this._cellClickedSubject = new Subject();
|
|
2847
|
-
/** @hidden */
|
|
2848
|
-
this._cellFocusedSubject = new Subject();
|
|
2849
|
-
/** @hidden */
|
|
2850
|
-
this._cellActivateSubject = new Subject();
|
|
2851
|
-
/** Stream that emits when the table cell being focused. */
|
|
2852
|
-
this.cellActivate$ = this._cellActivateSubject.asObservable();
|
|
2853
|
-
/** @hidden */
|
|
2854
|
-
this._toggleAllSelectableRowsSubject = new Subject();
|
|
2855
|
-
/** Stream that emits when toggling all selectable rows is needed. */
|
|
2856
|
-
this.toggleAllSelectableRows$ = this._toggleAllSelectableRowsSubject.asObservable();
|
|
2857
|
-
/** Stream that emits when table cell being clicked. */
|
|
2858
|
-
this.cellClicked$ = this._cellClickedSubject.asObservable();
|
|
2859
|
-
/** Stream that emits when scrolling to overlapped cell is needed. */
|
|
2860
|
-
this.scrollToOverlappedCell$ = this._scrollToOverlappedCellSubject.asObservable();
|
|
2861
|
-
/** Stream that emits when the table cell being focused. */
|
|
2862
|
-
this.cellFocused$ = this._cellFocusedSubject.asObservable();
|
|
2863
|
-
/** Toggle row stream. */
|
|
2864
|
-
this.toggleRow$ = this._toggleRowSubject.asObservable();
|
|
2865
|
-
/** Editable cells map. */
|
|
2866
|
-
this.editableCells = new Map();
|
|
2867
|
-
/** @hidden */
|
|
2868
|
-
this.childRowsAdded$ = new Subject();
|
|
2869
|
-
/** Stream to load child items for a particular rows. */
|
|
2870
|
-
this.loadChildRows$ = new Subject();
|
|
2871
|
-
}
|
|
2872
|
-
/** `toggleRow$` stream trigger. */
|
|
2873
|
-
toggleRow(evt) {
|
|
2874
|
-
this._toggleRowSubject.next(evt);
|
|
2875
|
-
}
|
|
2876
|
-
/** `scrollToOverlappedCell$` stream trigger. */
|
|
2877
|
-
scrollToOverlappedCell() {
|
|
2878
|
-
this._scrollToOverlappedCellSubject.next();
|
|
2879
|
-
}
|
|
2880
|
-
/** `cellClicked$` stream trigger. */
|
|
2881
|
-
cellClicked(evt) {
|
|
2882
|
-
this._cellClickedSubject.next(evt);
|
|
2883
|
-
}
|
|
2884
|
-
/** @hidden */
|
|
2885
|
-
cellActivate(columnIndex, row) {
|
|
2886
|
-
this._cellActivateSubject.next({ columnIndex, row: row.value });
|
|
2887
|
-
}
|
|
2888
|
-
/** `cellFocused$` stream trigger. */
|
|
2889
|
-
cellFocused(evt) {
|
|
2890
|
-
this._cellFocusedSubject.next(evt);
|
|
2891
|
-
}
|
|
2892
|
-
/** `toggleAllSelectableRows$` stream trigger. */
|
|
2893
|
-
toggleAllSelectableRows(selectAll) {
|
|
2894
|
-
this._toggleAllSelectableRowsSubject.next(selectAll);
|
|
2895
|
-
}
|
|
2896
|
-
/** Groups the table rows. */
|
|
2897
|
-
groupTableRows(sourceRows, groups, groupRulesMap) {
|
|
2898
|
-
const groupRules = Array.from(groups);
|
|
2899
|
-
if (!groupRules.length) {
|
|
2900
|
-
/**
|
|
2901
|
-
* In case if previously we had groups with collapsed items
|
|
2902
|
-
* but now we don't have it we need to reset row.hidden flag
|
|
2903
|
-
* in order to avoid empty table after groups settings removing
|
|
2904
|
-
*/
|
|
2905
|
-
sourceRows.forEach((row) => {
|
|
2906
|
-
row.hidden = false;
|
|
2907
|
-
});
|
|
2908
|
-
return sourceRows;
|
|
2909
|
-
}
|
|
2910
|
-
// Build tree like groups
|
|
2911
|
-
const treeLikeGroupedRows = createGroupedTableRowsTree(groupRules, sourceRows);
|
|
2912
|
-
// Sort tree like groups
|
|
2913
|
-
const sortedTreeLikeGroupedRows = sortTreeLikeGroupedRows(treeLikeGroupedRows, groupRulesMap);
|
|
2914
|
-
// Convert tree like list to a flat list
|
|
2915
|
-
return convertTreeLikeToFlatList(sortedTreeLikeGroupedRows);
|
|
2916
|
-
}
|
|
2917
|
-
/** Sets editable cells for particular row. */
|
|
2918
|
-
updateEditableCells(row, cells) {
|
|
2919
|
-
this.editableCells.set(row, cells);
|
|
2920
|
-
}
|
|
2921
|
-
/** Removes editable cells of particular row. */
|
|
2922
|
-
removeEditableCells(row) {
|
|
2923
|
-
this.editableCells.delete(row);
|
|
2924
|
-
}
|
|
2925
|
-
/** Triggers stream to load child items for a particular rows. */
|
|
2926
|
-
loadChildRows(rows) {
|
|
2927
|
-
const expandableRows = [];
|
|
2928
|
-
if (Array.isArray(rows)) {
|
|
2929
|
-
expandableRows.push(...rows.filter((r) => !r.childItemsLoading$.value));
|
|
2930
|
-
}
|
|
2931
|
-
else {
|
|
2932
|
-
if (rows.childItemsLoading$.value) {
|
|
2933
|
-
return;
|
|
2934
|
-
}
|
|
2935
|
-
expandableRows.push(rows);
|
|
2936
|
-
}
|
|
2937
|
-
this.loadChildRows$.next(expandableRows);
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
TableRowService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableRowService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2941
|
-
TableRowService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableRowService });
|
|
2942
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TableRowService, decorators: [{
|
|
2943
|
-
type: Injectable
|
|
2944
|
-
}] });
|
|
2945
|
-
|
|
2946
3162
|
class TableHeaderResizerDirective {
|
|
2947
3163
|
constructor() {
|
|
2948
3164
|
/** @hidden */
|