@progress/kendo-angular-pivotgrid 16.6.0-develop.2 → 16.6.0-develop.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,53 +2,207 @@
2
2
  * Copyright © 2024 Progress Software Corporation. All rights reserved.
3
3
  * Licensed under commercial license. See LICENSE.md in the project root for more information
4
4
  *-------------------------------------------------------------------------------------------*/
5
- import { Component, Input } from '@angular/core';
5
+ import { Component, ElementRef, Input, NgZone } from '@angular/core';
6
6
  import { Subscription } from 'rxjs';
7
7
  import { PivotGridDataService } from '../data-binding/pivotgrid-data.service';
8
8
  import { CellTemplateDirective } from './templates/pivotgrid-cell-template.directive';
9
9
  import { ValueCellTemplateDirective } from './templates/pivotgrid-value-cell-template.directive';
10
10
  import { ColumnHeaderCellTemplateDirective } from './templates/pivotgrid-column-header-cell-template.directive';
11
11
  import { RowHeaderCellTemplateDirective } from './templates/pivotgrid-row-header-cell-template.directive';
12
+ import { ScrollableTable } from '../virtual/scrollable-container';
12
13
  import { PivotGridCellDirective } from './pivotgrid-cell.directive';
13
- import { NgFor, NgIf } from '@angular/common';
14
+ import { NgFor, NgIf, NgStyle } from '@angular/common';
15
+ import { LocalizationService } from '@progress/kendo-angular-l10n';
16
+ import { PivotGridComponent } from '../pivotgrid.component';
17
+ import { Keys, isDocumentAvailable } from '@progress/kendo-angular-common';
18
+ import { isVisible, matchAriaAttributes } from '../util';
19
+ import { PivotGridScrollService } from '../virtual/scroll.service';
14
20
  import * as i0 from "@angular/core";
15
21
  import * as i1 from "../data-binding/pivotgrid-data.service";
22
+ import * as i2 from "@progress/kendo-angular-l10n";
23
+ import * as i3 from "../pivotgrid.component";
24
+ import * as i4 from "../virtual/scroll.service";
16
25
  /**
17
26
  * @hidden
18
27
  */
19
28
  export class PivotGridTableComponent {
20
- constructor(dataService) {
29
+ constructor(host, dataService, localization, pivotGrid, zone, scrollService) {
30
+ this.host = host;
21
31
  this.dataService = dataService;
22
- this.dataChangeSubs = new Subscription();
32
+ this.localization = localization;
33
+ this.pivotGrid = pivotGrid;
34
+ this.zone = zone;
35
+ this.scrollService = scrollService;
36
+ this.startRowIndex = 0;
37
+ this.startColIndex = 0;
38
+ this.rtl = false;
39
+ this.subs = new Subscription();
40
+ this.colsUpdateCallback = (cols) => {
41
+ this.renderedCols = Math.min(cols.length, this.scrollableSettings?.columns);
42
+ this.renderedCols && this.scrollable && (this.scrollable.totalCols = cols.length);
43
+ (this.scrollableSettings && this.scrollableSettings.type !== 'row') && this.scrollable?.onNewData(true);
44
+ this.headerItems = cols;
45
+ isDocumentAvailable() && !this.scrollable && this.tableType === 'values' && (this.columnVirtualization || this.rowVirtualization) && this.initScrollable();
46
+ };
47
+ this.initScrollable = () => {
48
+ this.scrollable = new ScrollableTable(this.host.nativeElement, {
49
+ onScroll: () => {
50
+ this.startRowIndex = this.scrollable.startRow;
51
+ this.startColIndex = this.scrollable.startCol;
52
+ this.renderedRows = this.rows.slice(this.startRowIndex, this.startRowIndex + this.scrollableSettings.rows);
53
+ this.scrollable.renderedRows = this.renderedRows.length;
54
+ this.scrollable.renderedCols = this.renderedCols;
55
+ },
56
+ onScrollEnd: () => {
57
+ matchAriaAttributes(this.host.nativeElement.closest('.k-pivotgrid'));
58
+ }
59
+ }, {
60
+ itemHeight: this.scrollableSettings.rowHeight,
61
+ itemWidth: this.colWidth || 200,
62
+ total: this.totalRows,
63
+ totalCols: this.headerItems.length,
64
+ renderedRows: this.scrollableSettings.rows,
65
+ renderedCols: this.scrollableSettings.columns,
66
+ columnVirtualization: this.columnVirtualization,
67
+ rowVirtualization: this.rowVirtualization,
68
+ rtl: this.rtl
69
+ });
70
+ };
71
+ this.subs.add(this.localization.changes.subscribe(({ rtl }) => {
72
+ this.rtl = rtl;
73
+ this.scrollable && (this.scrollable.rtl = rtl);
74
+ }));
23
75
  }
24
76
  get pivotGridId() {
25
77
  return `kendo-pivotgrid-${this.dataService.pivotGridId}-`;
26
78
  }
79
+ get columnVirtualization() {
80
+ return this.scrollableSettings?.type && this.scrollableSettings?.type !== 'row';
81
+ }
82
+ get rowVirtualization() {
83
+ return this.scrollableSettings?.type && this.scrollableSettings?.type !== 'column';
84
+ }
27
85
  ngOnInit() {
28
- this.dataChangeSubs.add(this.dataService[`${this.tableType}Rows`].subscribe(rows => this.rows = rows));
29
- this.dataChangeSubs.add(this.tableType === 'values' ?
30
- this.dataService.columnHeaderCols.subscribe(cols => this.headerItems = cols) :
31
- this.dataService[`${this.tableType}Cols`].subscribe(cols => this.headerItems = cols));
86
+ this.subs.add(this.dataService[`${this.tableType}Rows`].subscribe(rows => {
87
+ this.rows = rows;
88
+ this.renderedRows = this.scrollableSettings ? rows.slice(this.startRowIndex, this.startRowIndex + this.scrollableSettings.rows) : rows;
89
+ this.totalRows && this.totalRows !== rows.length && this.scrollable && (this.scrollable.total = rows.length);
90
+ this.scrollable && this.scrollable.onNewData(this.totalRows && this.totalRows !== rows.length);
91
+ this.totalRows = rows.length;
92
+ }));
93
+ this.subs.add(this.tableType === 'values' ?
94
+ this.dataService.columnHeaderCols.subscribe(this.colsUpdateCallback) :
95
+ this.dataService[`${this.tableType}Cols`].subscribe(this.colsUpdateCallback));
96
+ }
97
+ ngAfterViewInit() {
98
+ if (isDocumentAvailable() && this.scrollService.virtualScrolling) {
99
+ this.initScrollableKeyboardNavigation();
100
+ }
32
101
  }
33
102
  ngOnDestroy() {
34
- this.dataChangeSubs.unsubscribe();
103
+ this.subs.unsubscribe();
104
+ this.scrollable?.destroy();
105
+ }
106
+ initScrollableKeyboardNavigation() {
107
+ this.host.nativeElement.addEventListener('keydown', (e) => {
108
+ if (this.tableType === 'values' && e.target.tagName === 'TD') {
109
+ e.stopImmediatePropagation();
110
+ e.preventDefault();
111
+ if (e.keyCode === Keys.ArrowLeft) {
112
+ const id = e.target.getAttribute('id');
113
+ if (id.split('-')[5] === '1') {
114
+ const target = document.querySelector(`tr[aria-owns*="${id}"]`);
115
+ this.pivotGrid.navigation.focusElement(target.lastElementChild, e.target);
116
+ }
117
+ else {
118
+ this.pivotGrid.navigation.focusElement(e.target.previousElementSibling, e.target);
119
+ if (!isVisible(e.target.previousElementSibling, this.host.nativeElement, this.scrollable.offsetFirst, this.rtl).visibleX) {
120
+ e.target.previousElementSibling.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'start' });
121
+ }
122
+ }
123
+ }
124
+ else if (e.keyCode === Keys.ArrowRight) {
125
+ const id = e.target.getAttribute('id');
126
+ if (id.split('-')[5] !== this.headerItems.length.toString()) {
127
+ this.pivotGrid.navigation.focusElement(e.target.nextElementSibling, e.target);
128
+ if (!isVisible(e.target.nextElementSibling, this.host.nativeElement, this.scrollable.offsetFirst, this.rtl).visibleX) {
129
+ e.target.nextElementSibling.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'start' });
130
+ }
131
+ }
132
+ }
133
+ else if (e.keyCode === Keys.ArrowUp) {
134
+ const id = e.target.getAttribute('id');
135
+ if (id.split('-')[4] === '1') {
136
+ const target = document.getElementById(e.target.getAttribute('aria-describedby').split(' ').pop());
137
+ this.pivotGrid.navigation.focusElement(target, e.target);
138
+ }
139
+ else {
140
+ const index = Array.from(e.target.parentElement.children).findIndex(el => el === e.target);
141
+ const elementToFocus = e.target.parentElement.previousElementSibling.children[index];
142
+ this.pivotGrid.navigation.focusElement(elementToFocus, e.target);
143
+ if (!isVisible(elementToFocus, this.host.nativeElement, this.scrollable.offsetFirst, this.rtl).visibleY) {
144
+ elementToFocus.scrollIntoView();
145
+ }
146
+ }
147
+ }
148
+ else if (e.keyCode === Keys.ArrowDown) {
149
+ const id = e.target.getAttribute('id');
150
+ if (id.split('-')[4] !== this.totalRows.toString()) {
151
+ const index = Array.from(e.target.parentElement.children).findIndex(el => el === e.target);
152
+ const elementToFocus = e.target.parentElement.nextElementSibling.children[index];
153
+ this.pivotGrid.navigation.focusElement(elementToFocus, e.target);
154
+ if (!isVisible(elementToFocus, this.host.nativeElement, this.scrollable.offsetFirst, this.rtl).visibleY) {
155
+ elementToFocus.scrollIntoView(false);
156
+ }
157
+ }
158
+ }
159
+ }
160
+ if (this.tableType === 'rowHeader' && e.target.tagName === 'TH' && e.keyCode === Keys.ArrowRight) {
161
+ if (e.target.matches(':last-child')) {
162
+ e.stopImmediatePropagation();
163
+ e.preventDefault();
164
+ const valuesContainer = this.host.nativeElement.nextElementSibling;
165
+ valuesContainer.scrollLeft = this.rtl ? valuesContainer.scrollWidth : 0;
166
+ this.zone.runOutsideAngular(() => setTimeout(() => {
167
+ const elementToFocusId = e.target.parentElement.getAttribute('aria-owns').split(' ')[0];
168
+ const elementToFocus = document.getElementById(elementToFocusId);
169
+ this.pivotGrid.navigation.focusElement(elementToFocus, e.target);
170
+ }));
171
+ }
172
+ }
173
+ if (this.tableType === 'columnHeader' && e.target.tagName === 'TH' && e.keyCode === Keys.ArrowDown) {
174
+ if (e.target.parentElement.matches(':last-child')) {
175
+ e.stopImmediatePropagation();
176
+ e.preventDefault();
177
+ const valuesContainer = this.host.nativeElement.nextElementSibling.nextElementSibling;
178
+ valuesContainer.scrollTop = 0;
179
+ this.zone.runOutsideAngular(() => setTimeout(() => {
180
+ const elementToFocus = valuesContainer.querySelector(`td[aria-describedby*="${e.target.getAttribute('id')}"]`);
181
+ this.pivotGrid.navigation.focusElement(elementToFocus, e.target);
182
+ }));
183
+ }
184
+ }
185
+ }, true);
35
186
  }
36
187
  }
37
- PivotGridTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: PivotGridTableComponent, deps: [{ token: i1.PivotGridDataService }], target: i0.ɵɵFactoryTarget.Component });
38
- PivotGridTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: PivotGridTableComponent, isStandalone: true, selector: "kendo-pivotgrid-table", inputs: { tableType: "tableType", colWidth: "colWidth", customCellTemplate: "customCellTemplate", valueCellTemplate: "valueCellTemplate", rowHeaderCellTemplate: "rowHeaderCellTemplate", columnHeaderCellTemplate: "columnHeaderCellTemplate" }, ngImport: i0, template: `
188
+ PivotGridTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: PivotGridTableComponent, deps: [{ token: i0.ElementRef }, { token: i1.PivotGridDataService }, { token: i2.LocalizationService }, { token: i3.PivotGridComponent }, { token: i0.NgZone }, { token: i4.PivotGridScrollService }], target: i0.ɵɵFactoryTarget.Component });
189
+ PivotGridTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: PivotGridTableComponent, isStandalone: true, selector: "kendo-pivotgrid-table", inputs: { tableType: "tableType", colWidth: "colWidth", customCellTemplate: "customCellTemplate", valueCellTemplate: "valueCellTemplate", rowHeaderCellTemplate: "rowHeaderCellTemplate", columnHeaderCellTemplate: "columnHeaderCellTemplate", scrollableSettings: "scrollableSettings" }, ngImport: i0, template: `
39
190
  <table
40
191
  class="k-pivotgrid-table"
41
- role="presentation">
192
+ role="presentation"
193
+ [ngStyle]="{float: this.tableType === 'values' ? this.rtl ? 'right' : 'left' : 'initial'}">
42
194
  <colgroup>
195
+ <col *ngIf="tableType === 'values' && columnVirtualization && startColIndex > 0" [style.width]="startColIndex * (colWidth >= 0 ? colWidth : 200) + 'px'"/>
43
196
  <col
44
- *ngFor="let item of headerItems;"
197
+ *ngFor="let item of (tableType === 'values' && columnVirtualization ? headerItems?.slice(0, renderedCols) : headerItems);"
45
198
  [style.width]="tableType !== 'rowHeader' ? colWidth >= 0 ? colWidth + 'px' : '200px' : undefined" />
46
199
  </colgroup>
47
200
  <tbody class="k-pivotgrid-tbody" [attr.role]="tableType === 'values' ? 'none' : 'rowgroup'">
48
- <tr *ngFor="let row of rows; index as rowIndex"
201
+ <tr *ngFor="let row of (tableType === 'values' && rowVirtualization ? renderedRows : rows); index as rowIndex"
49
202
  class="k-pivotgrid-row"
50
203
  [attr.role]="tableType === 'values' ? 'none' : 'row'">
51
- <ng-container *ngFor="let cell of row.cells; index as colIndex">
204
+ <td *ngIf="tableType === 'values' && columnVirtualization && startColIndex > 0" class="k-pivotgrid-cell"></td>
205
+ <ng-container *ngFor="let cell of (tableType === 'values' && columnVirtualization ? row.cells.slice(startColIndex, (startColIndex + renderedCols)) : row.cells); index as colIndex">
52
206
  <th
53
207
  *ngIf="cell && tableType !== 'values'"
54
208
  [kendoPivotGridCell]="cell"
@@ -56,8 +210,8 @@ PivotGridTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0"
56
210
  [rowHeaderCellTemplate]="rowHeaderCellTemplate"
57
211
  [columnHeaderCellTemplate]="columnHeaderCellTemplate"
58
212
  [tableType]="tableType"
59
- [colIndex]="colIndex"
60
- [rowIndex]="rowIndex"
213
+ [colIndex]="colIndex + startColIndex"
214
+ [rowIndex]="rowIndex + startRowIndex"
61
215
  [attr.aria-expanded]="cell.hasChildren && cell.children.length ? 'true' : 'false'"
62
216
  [attr.role]="tableType === 'columnHeader' ? 'columnheader' : tableType === 'rowHeader' ? 'rowheader' : 'none'"
63
217
  [attr.id]="pivotGridId + (tableType === 'columnHeader' ? 'ch-' : 'rh-') + (rowIndex + 1) + '-' + (colIndex + 1)"></th>
@@ -67,15 +221,15 @@ PivotGridTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0"
67
221
  [valueCellTemplate]="valueCellTemplate"
68
222
  [kendoPivotGridCell]="cell"
69
223
  tableType="values"
70
- [colIndex]="colIndex"
71
- [rowIndex]="rowIndex"
224
+ [colIndex]="colIndex + startColIndex"
225
+ [rowIndex]="rowIndex + startRowIndex"
72
226
  role="gridcell"
73
- [attr.id]="pivotGridId + 'cell-' + (rowIndex + 1) + '-' + (colIndex + 1)"></td>
227
+ [attr.id]="pivotGridId + 'cell-' + (rowIndex + startRowIndex + 1) + '-' + (colIndex + startColIndex + 1)"></td>
74
228
  </ng-container>
75
229
  </tr>
76
230
  </tbody>
77
231
  </table>
78
- `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PivotGridCellDirective, selector: "[kendoPivotGridCell]", inputs: ["kendoPivotGridCell", "tableType", "rowIndex", "colIndex", "customCellTemplate", "valueCellTemplate", "rowHeaderCellTemplate", "columnHeaderCellTemplate"] }] });
232
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PivotGridCellDirective, selector: "[kendoPivotGridCell]", inputs: ["kendoPivotGridCell", "tableType", "rowIndex", "colIndex", "customCellTemplate", "valueCellTemplate", "rowHeaderCellTemplate", "columnHeaderCellTemplate"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
79
233
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: PivotGridTableComponent, decorators: [{
80
234
  type: Component,
81
235
  args: [{
@@ -83,17 +237,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
83
237
  template: `
84
238
  <table
85
239
  class="k-pivotgrid-table"
86
- role="presentation">
240
+ role="presentation"
241
+ [ngStyle]="{float: this.tableType === 'values' ? this.rtl ? 'right' : 'left' : 'initial'}">
87
242
  <colgroup>
243
+ <col *ngIf="tableType === 'values' && columnVirtualization && startColIndex > 0" [style.width]="startColIndex * (colWidth >= 0 ? colWidth : 200) + 'px'"/>
88
244
  <col
89
- *ngFor="let item of headerItems;"
245
+ *ngFor="let item of (tableType === 'values' && columnVirtualization ? headerItems?.slice(0, renderedCols) : headerItems);"
90
246
  [style.width]="tableType !== 'rowHeader' ? colWidth >= 0 ? colWidth + 'px' : '200px' : undefined" />
91
247
  </colgroup>
92
248
  <tbody class="k-pivotgrid-tbody" [attr.role]="tableType === 'values' ? 'none' : 'rowgroup'">
93
- <tr *ngFor="let row of rows; index as rowIndex"
249
+ <tr *ngFor="let row of (tableType === 'values' && rowVirtualization ? renderedRows : rows); index as rowIndex"
94
250
  class="k-pivotgrid-row"
95
251
  [attr.role]="tableType === 'values' ? 'none' : 'row'">
96
- <ng-container *ngFor="let cell of row.cells; index as colIndex">
252
+ <td *ngIf="tableType === 'values' && columnVirtualization && startColIndex > 0" class="k-pivotgrid-cell"></td>
253
+ <ng-container *ngFor="let cell of (tableType === 'values' && columnVirtualization ? row.cells.slice(startColIndex, (startColIndex + renderedCols)) : row.cells); index as colIndex">
97
254
  <th
98
255
  *ngIf="cell && tableType !== 'values'"
99
256
  [kendoPivotGridCell]="cell"
@@ -101,8 +258,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
101
258
  [rowHeaderCellTemplate]="rowHeaderCellTemplate"
102
259
  [columnHeaderCellTemplate]="columnHeaderCellTemplate"
103
260
  [tableType]="tableType"
104
- [colIndex]="colIndex"
105
- [rowIndex]="rowIndex"
261
+ [colIndex]="colIndex + startColIndex"
262
+ [rowIndex]="rowIndex + startRowIndex"
106
263
  [attr.aria-expanded]="cell.hasChildren && cell.children.length ? 'true' : 'false'"
107
264
  [attr.role]="tableType === 'columnHeader' ? 'columnheader' : tableType === 'rowHeader' ? 'rowheader' : 'none'"
108
265
  [attr.id]="pivotGridId + (tableType === 'columnHeader' ? 'ch-' : 'rh-') + (rowIndex + 1) + '-' + (colIndex + 1)"></th>
@@ -112,19 +269,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
112
269
  [valueCellTemplate]="valueCellTemplate"
113
270
  [kendoPivotGridCell]="cell"
114
271
  tableType="values"
115
- [colIndex]="colIndex"
116
- [rowIndex]="rowIndex"
272
+ [colIndex]="colIndex + startColIndex"
273
+ [rowIndex]="rowIndex + startRowIndex"
117
274
  role="gridcell"
118
- [attr.id]="pivotGridId + 'cell-' + (rowIndex + 1) + '-' + (colIndex + 1)"></td>
275
+ [attr.id]="pivotGridId + 'cell-' + (rowIndex + startRowIndex + 1) + '-' + (colIndex + startColIndex + 1)"></td>
119
276
  </ng-container>
120
277
  </tr>
121
278
  </tbody>
122
279
  </table>
123
280
  `,
124
281
  standalone: true,
125
- imports: [NgFor, NgIf, PivotGridCellDirective]
282
+ imports: [NgFor, NgIf, PivotGridCellDirective, NgStyle]
126
283
  }]
127
- }], ctorParameters: function () { return [{ type: i1.PivotGridDataService }]; }, propDecorators: { tableType: [{
284
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.PivotGridDataService }, { type: i2.LocalizationService }, { type: i3.PivotGridComponent }, { type: i0.NgZone }, { type: i4.PivotGridScrollService }]; }, propDecorators: { tableType: [{
128
285
  type: Input
129
286
  }], colWidth: [{
130
287
  type: Input
@@ -136,4 +293,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
136
293
  type: Input
137
294
  }], columnHeaderCellTemplate: [{
138
295
  type: Input
296
+ }], scrollableSettings: [{
297
+ type: Input
139
298
  }] } });
package/esm2020/util.mjs CHANGED
@@ -110,19 +110,19 @@ export const matchAriaAttributes = (wrapper) => {
110
110
  const firstColHeaderRowCellsIds = Array.from(firstColHeadersRow?.children).map(el => el.getAttribute('id')).join(' ');
111
111
  firstColHeadersRow.setAttribute('aria-owns', `${emptyCell.getAttribute('id')} ${firstColHeaderRowCellsIds}`);
112
112
  rowHeaderRows.forEach((row, index) => {
113
- const valueCellsIds = filterAndMap(Array.from(valueTableCells), c => c.getAttribute('id').split('-')[4] === (index + 1).toString(), c => c.getAttribute('id'));
113
+ const valueCellsIds = filterAndMap(Array.from(valueTableCells), c => c.getAttribute('id')?.split('-')[4] === (index + 1).toString(), c => c.getAttribute('id'));
114
114
  row.setAttribute('aria-owns', valueCellsIds.join(' '));
115
115
  });
116
116
  valueTableCells.forEach(cell => {
117
- const cellColIndex = +cell.getAttribute('id').split('-')[5];
117
+ const cellColIndex = +cell.getAttribute('id')?.split('-')[5];
118
118
  const colHeaderCellsIds = filterAndMap(Array.from(colHeaderCells), c => {
119
- const headerCellColIndex = +c.getAttribute('id').split('-')[5];
119
+ const headerCellColIndex = +c.getAttribute('id')?.split('-')[5];
120
120
  const headerCellColspan = +c.getAttribute('colspan');
121
121
  const colIndexIsEqual = cellColIndex === headerCellColIndex;
122
- const cellColIndexIsWithinHeaderCellRange = headerCellColspan > 1 && (headerCellColIndex + headerCellColspan - 1 >= cellColIndex);
122
+ const cellColIndexIsWithinHeaderCellRange = headerCellColIndex < cellColIndex && headerCellColspan > 1 && (headerCellColIndex + headerCellColspan - 1 >= cellColIndex);
123
123
  return colIndexIsEqual || cellColIndexIsWithinHeaderCellRange;
124
124
  }, c => c.getAttribute('id'));
125
- cell.setAttribute('aria-describedby', colHeaderCellsIds.join(' '));
125
+ colHeaderCellsIds.length && cell.setAttribute('aria-describedby', colHeaderCellsIds.join(' '));
126
126
  });
127
127
  };
128
128
  /**
@@ -197,3 +197,28 @@ export const swapItems = (arr, i1, i2) => {
197
197
  arr[i1] = arr[i2];
198
198
  arr[i2] = temp;
199
199
  };
200
+ /**
201
+ * @hidden
202
+ */
203
+ export const isVisible = (el, container, offsetY, rtl) => {
204
+ const elTop = el.offsetTop;
205
+ const elBottom = elTop + el.clientHeight;
206
+ let rect;
207
+ let containerRect;
208
+ if (rtl) {
209
+ rect = el.getBoundingClientRect();
210
+ containerRect = container.getBoundingClientRect();
211
+ }
212
+ const elLeft = rtl ? rect.left : el.offsetLeft;
213
+ const elRight = rtl ? rect.right : elLeft + el.clientWidth;
214
+ const containerTop = container.scrollTop;
215
+ const containerBottom = containerTop + container.clientHeight;
216
+ const containerLeft = rtl ? containerRect.left : container.scrollLeft;
217
+ const containerRight = rtl ? containerRect.right : containerLeft + container.clientWidth;
218
+ const visibleY = elTop >= containerTop - offsetY && elBottom <= containerBottom - offsetY;
219
+ const visibleX = rtl ? elRight <= containerRight && elLeft >= containerLeft : elLeft >= containerLeft && elRight <= containerRight;
220
+ return {
221
+ visibleX,
222
+ visibleY
223
+ };
224
+ };
@@ -0,0 +1,19 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2024 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ import { Injectable } from '@angular/core';
6
+ import * as i0 from "@angular/core";
7
+ /**
8
+ * @hidden
9
+ */
10
+ export class PivotGridScrollService {
11
+ constructor() {
12
+ this.virtualScrolling = false;
13
+ }
14
+ }
15
+ PivotGridScrollService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: PivotGridScrollService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
16
+ PivotGridScrollService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: PivotGridScrollService });
17
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: PivotGridScrollService, decorators: [{
18
+ type: Injectable
19
+ }] });
@@ -0,0 +1,114 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2024 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ /**
6
+ * @hidden
7
+ */
8
+ export class ScrollableTable {
9
+ constructor(element, handlers, options) {
10
+ this.element = element;
11
+ this.handlers = handlers;
12
+ this.options = options;
13
+ this.startRow = 0;
14
+ this.startCol = 0;
15
+ this.lastKnownScrollPosition = { top: 0, left: 0 };
16
+ this.offsetFirst = 0;
17
+ this.scrollHandler = (e) => {
18
+ const verticalDir = this.element.scrollTop - this.lastKnownScrollPosition.top;
19
+ const horizontalDir = Math.abs(this.element.scrollLeft) - this.lastKnownScrollPosition.left;
20
+ if (this.options.rowVirtualization) {
21
+ if (verticalDir > 0) {
22
+ if (this.element.scrollTop > (this.renderedRows * this.options.itemHeight + this.offsetFirst - this.rect.height)) {
23
+ this.startRow = Math.floor(this.element.scrollTop / this.options.itemHeight);
24
+ this.rowVirtualizationUpdate();
25
+ }
26
+ }
27
+ else {
28
+ if (this.element.scrollTop <= this.offsetFirst) {
29
+ this.startRow = Math.max(0, Math.ceil(this.element.scrollTop / this.options.itemHeight) - Math.ceil(this.options.renderedRows / 3));
30
+ this.rowVirtualizationUpdate();
31
+ }
32
+ }
33
+ }
34
+ if (this.options.columnVirtualization) {
35
+ if (horizontalDir > 0) {
36
+ if (Math.abs(this.element.scrollLeft) - (Math.max(this.startCol - 1, 0) * (this.options.colWidth || 200)) > (this.options.colWidth || 200)) {
37
+ this.startCol = Math.min(Math.max(Math.floor(Math.abs(this.element.scrollLeft) / this.options.itemWidth) - 1, 0), this.totalCols - this.renderedCols);
38
+ this.handlers.onScroll();
39
+ }
40
+ }
41
+ else {
42
+ if (Math.abs(this.element.scrollLeft) <= (this.startCol + 1) * (this.options.colWidth || 200)) {
43
+ this.startCol = Math.min(Math.max(Math.floor(Math.abs(this.element.scrollLeft) / this.options.itemWidth) - 1, 0), this.totalCols - this.renderedCols);
44
+ this.handlers.onScroll();
45
+ }
46
+ }
47
+ }
48
+ this.lastKnownScrollPosition = {
49
+ top: this.element.scrollTop,
50
+ left: Math.abs(this.element.scrollLeft)
51
+ };
52
+ };
53
+ this.scrollEndHandler = () => {
54
+ this.handlers.onScrollEnd();
55
+ };
56
+ this.initialize();
57
+ }
58
+ onNewData(recalculateSize = false) {
59
+ this.offsetFirst = this.startRow * this.options.itemHeight;
60
+ this.renderedRows = Math.min(this.options.renderedRows || Math.ceil(this.visibleRows * 3), this.total);
61
+ this.endRow = this.startRow + this.renderedRows;
62
+ this.renderedCols = Math.min(this.options.renderedCols || Math.ceil(this.visibleCols * 1.3), this.totalCols);
63
+ this.element.querySelector('table').style.transform = `translateY(${this.offsetFirst}px)`;
64
+ recalculateSize && this.recalculateSize();
65
+ }
66
+ destroy() {
67
+ this.element.removeEventListener('scroll', this.scrollHandler);
68
+ this.element.removeEventListener('scrollend', this.scrollEndHandler);
69
+ this.element.removeChild(this.stretcher);
70
+ }
71
+ initialize() {
72
+ this.rtl = this.options.rtl;
73
+ this.rect = this.element.getBoundingClientRect();
74
+ // visible rows and cols
75
+ this.visibleRows = Math.ceil(this.rect.height / this.options.itemHeight);
76
+ this.visibleCols = Math.ceil(this.rect.width / this.options.itemWidth);
77
+ // current totals
78
+ this.total = this.options.total;
79
+ this.totalCols = this.options.totalCols;
80
+ const totalHeight = this.total * this.options.itemHeight;
81
+ const totalWidth = this.totalCols * this.options.itemWidth;
82
+ // "page" size (rows and cols)
83
+ this.renderedRows = Math.min(this.options.renderedRows || Math.ceil(this.visibleRows * 3), this.total);
84
+ this.renderedCols = Math.min(this.options.renderedCols || Math.ceil(this.visibleCols * 1.3), this.totalCols);
85
+ // start and end row/col
86
+ this.startRow = 0;
87
+ this.startCol = 0;
88
+ this.endRow = this.startRow + this.renderedRows;
89
+ this.endCol = this.startCol + this.renderedCols;
90
+ // element that ensures correct scrolling dimensions of the container
91
+ this.stretcher = document.createElement('DIV');
92
+ this.stretcher.style.height = `${totalHeight}px`;
93
+ this.stretcher.style.width = `${totalWidth}px`;
94
+ this.element.appendChild(this.stretcher);
95
+ this.element.addEventListener('scroll', this.scrollHandler);
96
+ this.element.addEventListener('scrollend', this.scrollEndHandler);
97
+ }
98
+ recalculateSize() {
99
+ const totalHeight = this.total * this.options.itemHeight;
100
+ const totalWidth = this.totalCols * this.options.itemWidth;
101
+ this.stretcher.style.height = `${totalHeight}px`;
102
+ this.stretcher.style.width = `${totalWidth}px`;
103
+ this.rect = this.element.getBoundingClientRect();
104
+ // visible rows and cols
105
+ this.visibleRows = Math.ceil(this.rect.height / this.options.itemHeight);
106
+ this.visibleCols = Math.ceil(this.rect.width / this.options.itemWidth);
107
+ }
108
+ rowVirtualizationUpdate() {
109
+ this.endRow = Math.min(this.startRow + this.renderedRows, this.total);
110
+ this.offsetFirst = this.startRow * this.options.itemHeight;
111
+ this.element.querySelector('table').style.transform = `translateY(${this.offsetFirst}px)`;
112
+ this.handlers.onScroll();
113
+ }
114
+ }