@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.
Files changed (28) hide show
  1. package/esm2020/form/multi-combobox/multi-combobox/multi-combobox.component.mjs +3 -1
  2. package/esm2020/table/components/table-row/table-row.component.mjs +4 -3
  3. package/esm2020/table/table.component.mjs +43 -10
  4. package/esm2020/table-helpers/directives/table-virtual-scroll.directive.mjs +235 -37
  5. package/esm2020/table-helpers/models/directives.mjs +1 -1
  6. package/esm2020/table-helpers/services/table-column-resize.service.mjs +26 -5
  7. package/esm2020/table-helpers/table.mjs +1 -1
  8. package/fesm2015/fundamental-ngx-platform-form.mjs +2 -0
  9. package/fesm2015/fundamental-ngx-platform-form.mjs.map +1 -1
  10. package/fesm2015/fundamental-ngx-platform-table-helpers.mjs +363 -147
  11. package/fesm2015/fundamental-ngx-platform-table-helpers.mjs.map +1 -1
  12. package/fesm2015/fundamental-ngx-platform-table.mjs +165 -131
  13. package/fesm2015/fundamental-ngx-platform-table.mjs.map +1 -1
  14. package/fesm2020/fundamental-ngx-platform-form.mjs +2 -0
  15. package/fesm2020/fundamental-ngx-platform-form.mjs.map +1 -1
  16. package/fesm2020/fundamental-ngx-platform-table-helpers.mjs +364 -148
  17. package/fesm2020/fundamental-ngx-platform-table-helpers.mjs.map +1 -1
  18. package/fesm2020/fundamental-ngx-platform-table.mjs +164 -131
  19. package/fesm2020/fundamental-ngx-platform-table.mjs.map +1 -1
  20. package/fundamental-ngx-platform-v0.43.37.tgz +0 -0
  21. package/package.json +4 -4
  22. package/schematics/ng-add/index.js +1 -1
  23. package/table/table.component.d.ts +9 -0
  24. package/table-helpers/directives/table-virtual-scroll.directive.d.ts +64 -5
  25. package/table-helpers/models/directives.d.ts +1 -0
  26. package/table-helpers/services/table-column-resize.service.d.ts +4 -0
  27. package/table-helpers/table.d.ts +10 -2
  28. 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, RtlService, KeyUtil, resizeObservable, FDK_FOCUSABLE_ITEM_DIRECTIVE, FocusableItemDirective } from '@fundamental-ngx/cdk/utils';
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
- * Calculates rows in viewport.
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 (!this.virtualScroll || !this.bodyHeight) {
2365
- return;
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
- const rowHeight = this.rowHeight + 1;
2368
- const rowsVisible = this._table._tableRowsVisible;
2369
- const rowsInViewPort = this._table.getRowsInViewport();
2370
- const totalNodeCount = rowsVisible.length;
2371
- const scrollTop = this._table.tableScrollable.getScrollTop();
2372
- let startNodeIndex = Math.floor(scrollTop / rowHeight) - this.renderAhead;
2373
- startNodeIndex = Math.max(0, startNodeIndex);
2374
- let visibleNodeCount = Math.ceil(this._table.tableContainer.nativeElement.clientHeight / rowHeight) + 2 * this.renderAhead;
2375
- visibleNodeCount = Math.min(totalNodeCount - startNodeIndex, visibleNodeCount);
2376
- this.virtualScrollTransform$.next(startNodeIndex * rowHeight);
2377
- // Simple caching to avoid unnecessary re-renderings
2378
- const isCached = startNodeIndex === this._virtualScrollCache.startNodeIndex &&
2379
- visibleNodeCount === this._virtualScrollCache.visibleNodeCount &&
2380
- totalNodeCount === this._virtualScrollCache.totalNodeCount &&
2381
- // On rows change, even if the total number of rows is the same, the row object will be different
2382
- startNodeIndex === rowsInViewPort[0];
2383
- if (isCached) {
2384
- return;
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
- * Initialises scroll listener.
2627
+ * @hidden
2628
+ * Initialises scroll listeners.
2392
2629
  */
2393
2630
  listenOnVirtualScroll() {
2394
- this._tableScrollDispatcher
2395
- .verticallyScrolled()
2396
- .pipe(filter$1(() => this.virtualScroll && !!this.bodyHeight), takeUntil(this._destroy$))
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
- const calculatedWidth = this._fixedColumnsWidthMap.get(columnName);
2669
- return calculatedWidth || 'unset';
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 */