@genesislcap/grid-pro 14.284.2 → 14.284.3-alpha-fa8a336.0

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.
@@ -1,7 +1,7 @@
1
1
  import { __awaiter, __decorate } from "tslib";
2
2
  import { Events, } from '@ag-grid-community/core';
3
3
  import { dataServerResultFilter, MessageType, normaliseCriteria, toFieldMetadata, } from '@genesislcap/foundation-comms';
4
- import { LifecycleMixin } from '@genesislcap/foundation-utils';
4
+ import { LifecycleMixin, insertDocumentCSSRule } from '@genesislcap/foundation-utils';
5
5
  import { attr, customElement, DOM, observable } from '@microsoft/fast-element';
6
6
  import { gridProGenesisDatasourceEventNames } from '../grid-pro-genesis-datasource';
7
7
  import { logger } from '../utils';
@@ -12,9 +12,58 @@ import { ReqRepServerSideDatasource } from './server-side.resource-reqrep';
12
12
  const criteriaDelimiter = ';';
13
13
  const withoutColumnDefs = null;
14
14
  const timeoutInMs = 500;
15
+ /**
16
+ * Custom CSS for server-side datasource sort indicators
17
+ * Makes sort indicators always visible with custom styling
18
+ */
19
+ const serverSideSortIndicatorCSS = `
20
+ .grid-pro-server-side .ag-header-cell-sortable .ag-sort-indicator-container {
21
+ opacity: 1 !important;
22
+ visibility: visible !important;
23
+ }
24
+
25
+ .grid-pro-server-side .ag-header-cell-sortable:not(.ag-header-cell-sorted) .ag-sort-indicator-container {
26
+ opacity: 0.4 !important;
27
+ }
28
+
29
+ .grid-pro-server-side .ag-header-cell-sorted .ag-sort-indicator-container {
30
+ opacity: 1 !important;
31
+ }
32
+
33
+ .grid-pro-server-side .ag-sort-ascending-icon::before,
34
+ .grid-pro-server-side .ag-sort-descending-icon::before,
35
+ .grid-pro-server-side .ag-header-cell-sortable:not(.ag-header-cell-sorted) .ag-sort-indicator-icon::before {
36
+ font-family: var(--ag-icon-font-family, agGridAlpine) !important;
37
+ font-size: var(--ag-icon-size, 16px) !important;
38
+ color: var(--ag-header-foreground-color, var(--ag-foreground-color)) !important;
39
+ }
40
+
41
+ .grid-pro-server-side .ag-sort-ascending-icon::before {
42
+ content: "\\e90d" !important; /* AG Grid sort up icon */
43
+ }
44
+
45
+ .grid-pro-server-side .ag-sort-descending-icon::before {
46
+ content: "\\e90c" !important; /* AG Grid sort down icon */
47
+ }
48
+
49
+ .grid-pro-server-side .ag-header-cell-sortable:not(.ag-header-cell-sorted) .ag-sort-indicator-icon::before {
50
+ content: "\\e90b" !important; /* AG Grid sort icon */
51
+ opacity: 0.6 !important;
52
+ }
53
+
54
+ /* Ensure the sort indicator is always visible for sortable columns in server-side mode */
55
+ .grid-pro-server-side .ag-header-cell-sortable .ag-sort-indicator-icon {
56
+ display: inline-block !important;
57
+ }
58
+ `;
15
59
  /**
16
60
  * A Genesis Datasource element, for server-side | SSRM-compatible data fetching and used exclusively by the GridPro element.
17
61
  * @remarks Only supports Server-Side Row Model. Requires `@ag-grid-enterprise/server-side-row-model` setup and valid AG Grid Enterprise license.
62
+ *
63
+ * **Custom Sort Indicators**: This datasource automatically applies custom sort indicators that are always visible (instead of only on hover).
64
+ * Sortable columns will show a subtle sort icon even when not sorted, and active sort indicators will be more prominent.
65
+ * The custom styling uses AG Grid's native icon font for consistency with the grid theme.
66
+ *
18
67
  * @alpha
19
68
  */
20
69
  let GridProServerSideDatasource = class GridProServerSideDatasource extends LifecycleMixin(GridProBaseDatasource) {
@@ -33,6 +82,7 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
33
82
  */
34
83
  this.liveUpdates = false;
35
84
  this.request = {};
85
+ this.sortIndicatorCSSDestroy = null;
36
86
  }
37
87
  resourceNameChanged(oldValue, newValue) {
38
88
  if (!oldValue || oldValue === newValue)
@@ -45,7 +95,7 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
45
95
  const criteriaIsNotDuplicate = oldCriteria !== normaliseCriteria(newCriteria, criteriaDelimiter);
46
96
  if (this.ssrmDatasource && criteriaIsNotDuplicate) {
47
97
  DOM.queueUpdate(() => __awaiter(this, void 0, void 0, function* () {
48
- yield this.reloadResourceData();
98
+ yield this.restart();
49
99
  }));
50
100
  }
51
101
  }
@@ -87,13 +137,12 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
87
137
  // Cache the current filter model before clearing data to preserve column filters
88
138
  yield ((_b = this.agGrid) === null || _b === void 0 ? void 0 : _b.cacheFilterConfig());
89
139
  this.clearRowData(withoutColumnDefs);
140
+ (_c = this.agGrid.gridApi) === null || _c === void 0 ? void 0 : _c.refreshServerSide({ purge: true });
90
141
  if (params) {
91
- // this.datasource.stream = undefined;
92
142
  const options = Object.assign(Object.assign({}, this.datasourceOptions()), params);
93
143
  const initOK = yield this.datasource.init(options, true, false);
94
- // this.$emit(gridProGenesisDatasourceEventNames.dataInit);
95
144
  if (!initOK) {
96
- (_c = this.agGrid.gridApi) === null || _c === void 0 ? void 0 : _c.setServerSideDatasource(null);
145
+ (_d = this.agGrid.gridApi) === null || _d === void 0 ? void 0 : _d.setServerSideDatasource(null);
97
146
  logger.error('Genesis Datasource re-init failed on filtering/sorting.');
98
147
  }
99
148
  }
@@ -102,13 +151,48 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
102
151
  yield this.ssrmDatasource.destroy();
103
152
  this.ssrmDatasource = undefined;
104
153
  }
105
- (_d = this.agGrid.gridApi) === null || _d === void 0 ? void 0 : _d.setServerSideDatasource(null);
106
- (_e = this.agGrid.gridApi) === null || _e === void 0 ? void 0 : _e.refreshServerSide({ purge: true });
154
+ (_e = this.agGrid.gridApi) === null || _e === void 0 ? void 0 : _e.setServerSideDatasource(null);
107
155
  (_g = (_f = this.agGrid) === null || _f === void 0 ? void 0 : _f.gridApi) === null || _g === void 0 ? void 0 : _g.showLoadingOverlay();
108
156
  yield this.init();
109
157
  }
110
158
  });
111
159
  }
160
+ /**
161
+ * Applies custom sort indicator styling for server-side datasource
162
+ * @internal
163
+ */
164
+ applyServerSideSortIndicatorStyling() {
165
+ var _a, _b;
166
+ // Remove existing styling if present
167
+ this.removeServerSideSortIndicatorStyling();
168
+ // Add custom CSS class to the grid container
169
+ if ((_a = this.agGrid) === null || _a === void 0 ? void 0 : _a.gridApi) {
170
+ // Add CSS class to grid element
171
+ const gridElement = ((_b = this.agGrid.gridSlot) === null || _b === void 0 ? void 0 : _b.parentElement) || this.agGrid;
172
+ if (gridElement) {
173
+ gridElement.classList.add('grid-pro-server-side');
174
+ }
175
+ }
176
+ // Insert custom CSS rules
177
+ this.sortIndicatorCSSDestroy = insertDocumentCSSRule(serverSideSortIndicatorCSS, 'grid-pro-server-side-sort');
178
+ }
179
+ /**
180
+ * Removes custom sort indicator styling
181
+ * @internal
182
+ */
183
+ removeServerSideSortIndicatorStyling() {
184
+ var _a, _b;
185
+ // Remove CSS class from grid element
186
+ const gridElement = ((_b = (_a = this.agGrid) === null || _a === void 0 ? void 0 : _a.gridSlot) === null || _b === void 0 ? void 0 : _b.parentElement) || this.agGrid;
187
+ if (gridElement) {
188
+ gridElement.classList.remove('grid-pro-server-side');
189
+ }
190
+ // Remove custom CSS rules
191
+ if (this.sortIndicatorCSSDestroy) {
192
+ this.sortIndicatorCSSDestroy();
193
+ this.sortIndicatorCSSDestroy = null;
194
+ }
195
+ }
112
196
  init() {
113
197
  return __awaiter(this, void 0, void 0, function* () {
114
198
  if (this.agGrid) {
@@ -126,55 +210,71 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
126
210
  filter: true,
127
211
  resizable: true,
128
212
  sortable: false,
129
- }, pagination: this.pagination, paginationPageSize: this.maxRows, rowBuffer: 0, rowModelType: 'serverSide', suppressServerSideInfiniteScroll: false, suppressMultiSort: true, onFilterChanged: (params) => { }, onSortChanged: (params) => { }, onPaginationChanged: (event) => this.onPaginationChanged(event) }, this.agGrid['agGridOptions']), this.deferredGridOptions);
213
+ unSortIcon: true,
214
+ }, rowBuffer: 0, rowModelType: 'serverSide', suppressServerSideInfiniteScroll: false, suppressMultiSort: true, onFilterChanged: (params) => { }, onSortChanged: (params) => { }, onPaginationChanged: (event) => this.onPaginationChanged(event) }, this.agGrid['agGridOptions']), this.deferredGridOptions);
130
215
  this.agGrid.gridOptions = gridOptions;
131
- this.agGrid['initGrid']();
132
- this.agGrid.addEventListener(Events.EVENT_GRID_READY, () => __awaiter(this, void 0, void 0, function* () {
133
- const initOK = yield this.datasource.init(this.datasourceOptions(), true, false);
134
- if (!initOK) {
135
- logger.debug(`Genesis Datasource init failed for ${this.resourceName}`);
136
- this.clearRowData();
137
- return;
138
- }
139
- this.$emit(gridProGenesisDatasourceEventNames.dataInit);
140
- this.indexes = this.getResourceIndexes(this.datasource.availableIndexes);
141
- const fieldMetadata = toFieldMetadata(this.datasource.originalFieldDef);
142
- const agColumnDefs = yield this.getAgColumnDefs(fieldMetadata);
143
- this.agGrid.gridApi.setColumnDefs(agColumnDefs);
144
- if (this.isRequestServer) {
145
- this.ssrmDatasource = new ReqRepServerSideDatasource({
146
- createReqRepRequestFunc: this.createReqRepRequest.bind(this),
147
- reloadResourceDataFunc: this.reloadResourceData.bind(this),
148
- resourceName: this.resourceName,
149
- resourceParams: this.params,
150
- resourceIndexes: this.indexes,
151
- resourceColDefs: this.datasource.originalFieldDef,
152
- maxRows: +this.maxRows,
153
- maxView: +this.maxView,
154
- rowId: this.rowId,
155
- pagination: this.pagination,
156
- zeroBasedViewNumber: this.zeroBasedViewNumber,
157
- });
158
- }
159
- else {
160
- this.ssrmDatasource = new DataserverServerSideDatasource({
161
- createDataserverStreamFunc: this.createDataserverStream.bind(this),
162
- reloadResourceDataFunc: this.reloadResourceData.bind(this),
163
- resourceName: this.resourceName,
164
- resourceParams: this.params,
165
- resourceIndexes: this.indexes,
166
- resourceColDefs: this.datasource.originalFieldDef,
167
- maxRows: +this.maxRows,
168
- maxView: +this.maxView,
169
- rowId: this.rowId,
170
- pagination: this.pagination,
171
- zeroBasedViewNumber: this.zeroBasedViewNumber,
172
- });
173
- }
174
- this.agGrid.gridApi.setServerSideDatasource(this.ssrmDatasource);
175
- // Restore cached filter config after setting the server-side datasource
176
- this.agGrid.restoreCachedFilterConfig();
177
- }), { once: true });
216
+ // Guard against test environment where initGrid might not exist
217
+ if (typeof this.agGrid['initGrid'] === 'function') {
218
+ this.agGrid['initGrid']();
219
+ }
220
+ // Guard against test environment where addEventListener might not exist
221
+ if (typeof this.agGrid.addEventListener === 'function') {
222
+ this.agGrid.addEventListener(Events.EVENT_GRID_READY, () => __awaiter(this, void 0, void 0, function* () {
223
+ var _a, _b;
224
+ const initOK = yield this.datasource.init(this.datasourceOptions(), true, false);
225
+ if (!initOK) {
226
+ logger.debug(`Genesis Datasource init failed for ${this.resourceName}`);
227
+ this.clearRowData();
228
+ return;
229
+ }
230
+ this.$emit(gridProGenesisDatasourceEventNames.dataInit);
231
+ this.indexes = this.getResourceIndexes(this.datasource.availableIndexes);
232
+ const fieldMetadata = toFieldMetadata(this.datasource.originalFieldDef);
233
+ const agColumnDefs = yield this.getAgColumnDefs(fieldMetadata);
234
+ // Guard against test environment where setColumnDefs might not exist
235
+ if (typeof ((_a = this.agGrid.gridApi) === null || _a === void 0 ? void 0 : _a.setColumnDefs) === 'function') {
236
+ this.agGrid.gridApi.setColumnDefs(agColumnDefs);
237
+ }
238
+ if (this.isRequestServer) {
239
+ this.ssrmDatasource = new ReqRepServerSideDatasource({
240
+ createReqRepRequestFunc: this.createReqRepRequest.bind(this),
241
+ reloadResourceDataFunc: this.reloadResourceData.bind(this),
242
+ resourceName: this.resourceName,
243
+ resourceParams: this.params,
244
+ resourceIndexes: this.indexes,
245
+ resourceColDefs: this.datasource.originalFieldDef,
246
+ maxRows: +this.maxRows,
247
+ maxView: +this.maxView,
248
+ rowId: this.rowId,
249
+ pagination: this.pagination,
250
+ zeroBasedViewNumber: this.zeroBasedViewNumber,
251
+ });
252
+ }
253
+ else {
254
+ this.ssrmDatasource = new DataserverServerSideDatasource({
255
+ createDataserverStreamFunc: this.createDataserverStream.bind(this),
256
+ reloadResourceDataFunc: this.reloadResourceData.bind(this),
257
+ resourceName: this.resourceName,
258
+ resourceParams: this.params,
259
+ resourceIndexes: this.indexes,
260
+ resourceColDefs: this.datasource.originalFieldDef,
261
+ maxRows: +this.maxRows,
262
+ maxView: +this.maxView,
263
+ rowId: this.rowId,
264
+ pagination: this.pagination,
265
+ zeroBasedViewNumber: this.zeroBasedViewNumber,
266
+ });
267
+ }
268
+ // Guard against test environment where setServerSideDatasource might not exist
269
+ if (typeof ((_b = this.agGrid.gridApi) === null || _b === void 0 ? void 0 : _b.setServerSideDatasource) === 'function') {
270
+ this.agGrid.gridApi.setServerSideDatasource(this.ssrmDatasource);
271
+ }
272
+ // Restore cached filter config after setting the server-side datasource
273
+ this.agGrid.restoreCachedFilterConfig();
274
+ // Apply custom sort indicator styling for server-side datasource
275
+ this.applyServerSideSortIndicatorStyling();
276
+ }), { once: true });
277
+ }
178
278
  return;
179
279
  }
180
280
  logger.warn(`Application not connected, falling back to local columnDefs/rowData`);
@@ -183,6 +283,9 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
183
283
  onPaginationChanged(event) {
184
284
  var _a;
185
285
  if (this.agGrid.gridApi && this.ssrmDatasource) {
286
+ // event.newPage = true means "navigated to a new page" (NOT a page size change)
287
+ // event.newPage = false means pagination changed for other reasons (like page size change)
288
+ // this.ssrmDatasource.isNewPageSize = !event.newPage;
186
289
  this.ssrmDatasource.isNewPageSize = (_a = event.newPage) !== null && _a !== void 0 ? _a : false;
187
290
  }
188
291
  }
@@ -197,8 +300,15 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
197
300
  yield this.ssrmDatasource.destroy();
198
301
  this.ssrmDatasource = undefined;
199
302
  }
200
- (_b = this.agGrid.gridApi) === null || _b === void 0 ? void 0 : _b.setServerSideDatasource(null);
201
- (_c = this.agGrid.gridApi) === null || _c === void 0 ? void 0 : _c.refreshServerSide({ purge: true });
303
+ // Remove custom sort indicator styling
304
+ this.removeServerSideSortIndicatorStyling();
305
+ // Guard against test environment where gridApi methods might not exist
306
+ if (typeof ((_b = this.agGrid.gridApi) === null || _b === void 0 ? void 0 : _b.setServerSideDatasource) === 'function') {
307
+ this.agGrid.gridApi.setServerSideDatasource(null);
308
+ }
309
+ if (typeof ((_c = this.agGrid.gridApi) === null || _c === void 0 ? void 0 : _c.refreshServerSide) === 'function') {
310
+ this.agGrid.gridApi.refreshServerSide({ purge: true });
311
+ }
202
312
  });
203
313
  }
204
314
  restart() {
@@ -211,13 +321,21 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
211
321
  var _a, _b, _c, _d;
212
322
  this.rowData = new Map();
213
323
  if (withColumnDefs) {
214
- (_b = (_a = this.agGrid) === null || _a === void 0 ? void 0 : _a.gridApi) === null || _b === void 0 ? void 0 : _b.setColumnDefs([]);
324
+ // Guard against test environment where setColumnDefs might not exist
325
+ if (typeof ((_b = (_a = this.agGrid) === null || _a === void 0 ? void 0 : _a.gridApi) === null || _b === void 0 ? void 0 : _b.setColumnDefs) === 'function') {
326
+ this.agGrid.gridApi.setColumnDefs([]);
327
+ }
215
328
  }
216
329
  this.agTransaction = { remove: [] };
217
- (_c = this.agGrid.gridApi) === null || _c === void 0 ? void 0 : _c.forEachNode((node) => {
218
- this.agTransaction.remove.push(node.data);
219
- });
220
- (_d = this.agGrid.gridApi) === null || _d === void 0 ? void 0 : _d.applyServerSideTransaction(this.agTransaction);
330
+ // Guard against test environment where gridApi methods might not exist
331
+ if (typeof ((_c = this.agGrid.gridApi) === null || _c === void 0 ? void 0 : _c.forEachNode) === 'function') {
332
+ this.agGrid.gridApi.forEachNode((node) => {
333
+ this.agTransaction.remove.push(node.data);
334
+ });
335
+ }
336
+ if (typeof ((_d = this.agGrid.gridApi) === null || _d === void 0 ? void 0 : _d.applyServerSideTransaction) === 'function') {
337
+ this.agGrid.gridApi.applyServerSideTransaction(this.agTransaction);
338
+ }
221
339
  this.agTransaction = undefined;
222
340
  }
223
341
  getResourceIndexes(avaialbleIndexes) {
@@ -246,7 +364,6 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
246
364
  logger.warn('Specified [orderBy] index is not valid and will not be used. See https://learn.genesis.global/docs/database/data-types/index-entities/ - Available indexes:', validIndexes);
247
365
  }
248
366
  }
249
- logger.debug('Getting configured params:', params);
250
367
  return params;
251
368
  }
252
369
  get rowModel() {
@@ -280,7 +397,7 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
280
397
  // this.handleStreamDeletes(nextMessage.deletes);
281
398
  // this.ssrmDatasource.client_ROWS_COUNT -= nextMessage.deletes.length ?? 0;
282
399
  // this.ssrmDatasource.server_ROWS_COUNT -= nextMessage.deletes.length ?? 0;
283
- if (this.agGrid.pagination) {
400
+ if (this.pagination) {
284
401
  this.applyAllAgTransactions();
285
402
  }
286
403
  else {
@@ -318,10 +435,8 @@ let GridProServerSideDatasource = class GridProServerSideDatasource extends Life
318
435
  getAgColumnDefs(fieldsMetadata) {
319
436
  return __awaiter(this, void 0, void 0, function* () {
320
437
  const colDefsFromGenesisData = this.generateColumnDefsFromMetadata(fieldsMetadata, getServerSideFilterParamsByFieldType);
321
- colDefsFromGenesisData.map((colDef) => {
322
- return {
323
- sortable: [].concat(...this.indexes.values()).includes(colDef.field),
324
- };
438
+ colDefsFromGenesisData.forEach((colDef) => {
439
+ colDef.sortable = [].concat(...this.indexes.values()).includes(colDef.field);
325
440
  });
326
441
  const colDefsMergedWithTemplateDefs = yield this.agGrid.mergeAllColumnDefsAndStates(colDefsFromGenesisData, true);
327
442
  return colDefsMergedWithTemplateDefs;
@@ -66,7 +66,6 @@ export class BaseServerSideDatasource {
66
66
  return;
67
67
  }
68
68
  else if (toBeAppliedSortModel.length > 0) {
69
- this.currentSortModel = toBeAppliedSortModel;
70
69
  const coldIdBeingSorted = toBeAppliedSortModel[0].colId; // Not allowing multiple sorts by user
71
70
  const sortTypeBeingApplied = toBeAppliedSortModel[0].sort;
72
71
  const orderByAndToBeSortedColIds = this.getOrderByAndToBeSortedColIds(this.resourceIndexes, coldIdBeingSorted);
@@ -81,6 +80,7 @@ export class BaseServerSideDatasource {
81
80
  return;
82
81
  }
83
82
  else if (JSON.stringify(toBeAppliedSortModel) !== JSON.stringify(this.currentSortModel)) {
83
+ this.currentSortModel = toBeAppliedSortModel;
84
84
  this.resourceParams.ORDER_BY = orderByAndToBeSortedColIds.orderBy;
85
85
  this.resourceParams.REVERSE = sortTypeBeingApplied === 'desc' ? true : false;
86
86
  yield this.refreshDatasource(params);
@@ -49,7 +49,8 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
49
49
  this.dataserverStreamSubscription = this.dataserverStream.subscribe((dataserverResult) => {
50
50
  if (applyResult) {
51
51
  DOM.queueUpdate(() => __awaiter(this, void 0, void 0, function* () {
52
- if (this.resourceParams.CRITERIA_MATCH) {
52
+ // TODO: this is a bit of hack, due GSF not returning a different ROWS_COUNT when there is a CRITERIA_MATCH
53
+ if (this.resourceParams.CRITERIA_MATCH && this.pagination) {
53
54
  const updatedInfo = yield this.connect.snapshot(this.resourceName, {
54
55
  MAX_ROWS: this.maxView,
55
56
  MAX_VIEW: this.maxView,
@@ -135,14 +135,6 @@ export class GridPro extends LifecycleMixin(FoundationElement) {
135
135
  * @see https://www.ag-grid.com/javascript-data-grid/server-side-model-pagination/
136
136
  */
137
137
  this.pagination = false;
138
- /**
139
- * Number of rows per page when pagination is enabled.
140
- * @remarks
141
- * **Default Value:** `DatasourceDefaults.DEFAULT_PAGINATION_PAGE_SIZE (25)` (default MAX_ROWS divided by 10)
142
- * Only used when pagination is enabled.
143
- * Note that AG Grid's full pagination functionality requires the Enterprise module.
144
- */
145
- this.paginationPageSize = DatasourceDefaults.DEFAULT_PAGINATION_PAGE_SIZE;
146
138
  /**
147
139
  * Configuration for the grid status bar components.
148
140
  * @remarks
@@ -229,6 +221,9 @@ export class GridPro extends LifecycleMixin(FoundationElement) {
229
221
  this.rootEventsListeners.forEach(({ key, action }) => {
230
222
  this.addEventListener(key, action);
231
223
  });
224
+ this.paginationPageSize = this.isServerSide
225
+ ? this.gridProDatasource.maxRows
226
+ : DatasourceDefaults.DEFAULT_PAGINATION_PAGE_SIZE;
232
227
  }
233
228
  disconnectedCallback() {
234
229
  super.disconnectedCallback();
@@ -364,6 +359,12 @@ export class GridPro extends LifecycleMixin(FoundationElement) {
364
359
  get gridOptions() {
365
360
  return this.agGridOptions;
366
361
  }
362
+ /**
363
+ * @public
364
+ */
365
+ get isServerSide() {
366
+ return this.gridProDatasource instanceof GridProServerSideDatasource;
367
+ }
367
368
  /**
368
369
  * @public
369
370
  */
@@ -555,30 +556,29 @@ export class GridPro extends LifecycleMixin(FoundationElement) {
555
556
  return colDefsToReturn;
556
557
  });
557
558
  }
558
- setupPaginationAndStatusBar(gridOptions) {
559
- var _a;
560
- // TODO: when this happens "pagination" is undefined, (due init cycles, DS being child of grid) so we need to use this['_pagination'] for now
561
- const pagination = this['_pagination'];
562
- if (pagination && gridOptions.rowModelType) {
563
- gridOptions.pagination = true;
564
- gridOptions.paginationPageSize = this.paginationPageSize;
565
- gridOptions.suppressScrollOnNewData = true;
559
+ panelExists(statusPanelType, panels) {
560
+ return panels.some((panel) => panel.statusPanel === statusPanelType);
561
+ }
562
+ addRowCountPanel(statusPanels) {
563
+ // For server-side models, AG Grid's built-in row count components don't work properly
564
+ // because they expect access to the complete dataset. Instead, rely on our custom
565
+ // pagination component which properly handles server-side row counts.
566
+ if (this.isServerSide) {
567
+ return;
566
568
  }
567
- // Set default status bar with components based on configuration
568
- if (this.withStatusBar && this.statusBarConfig) {
569
- // Create status panels based on config
570
- const statusPanels = [];
571
- // Always add row count component
572
- if (this.statusBarConfig && this.statusBarConfig.rows === true) {
569
+ if (this.statusBarConfig && this.statusBarConfig.rows === true) {
570
+ const rowCountStatusPanel = 'agTotalAndFilteredRowCountComponent';
571
+ if (!this.panelExists(rowCountStatusPanel, statusPanels)) {
573
572
  statusPanels.push({
574
- statusPanel: gridOptions.rowModelType === 'serverSide'
575
- ? 'agTotalRowCountComponent'
576
- : 'agTotalAndFilteredRowCountComponent',
573
+ statusPanel: rowCountStatusPanel,
577
574
  align: 'left',
578
575
  });
579
576
  }
580
- // Add max rows component if explicitly enabled
581
- if (this.statusBarConfig && this.statusBarConfig.maxRows === true) {
577
+ }
578
+ }
579
+ addMaxRowsPanel(statusPanels) {
580
+ if (this.statusBarConfig && this.statusBarConfig.maxRows === true) {
581
+ if (!this.panelExists(GridProStatusBarTypes.labelValue, statusPanels)) {
582
582
  statusPanels.push({
583
583
  statusPanel: GridProStatusBarTypes.labelValue,
584
584
  statusPanelParams: {
@@ -589,31 +589,64 @@ export class GridPro extends LifecycleMixin(FoundationElement) {
589
589
  align: 'left',
590
590
  });
591
591
  }
592
- // Add aggregation component if explicitly enabled
593
- if (this.statusBarConfig && this.statusBarConfig.aggregation === true) {
592
+ }
593
+ }
594
+ addAggregationPanel(statusPanels) {
595
+ if (this.statusBarConfig && this.statusBarConfig.aggregation === true) {
596
+ if (!this.panelExists('agAggregationComponent', statusPanels)) {
594
597
  statusPanels.push({
595
598
  statusPanel: 'agAggregationComponent',
596
599
  align: 'right',
597
600
  });
598
601
  }
599
- // Add datasource-specific status bar components
600
- if (this.gridProDatasource) {
601
- const isServerSide = gridOptions.rowModelType === 'serverSide';
602
- const datasourceStatusPanels = this.gridProDatasource.getDatasourceStatusBarPanels(isServerSide);
603
- statusPanels.push(...datasourceStatusPanels);
604
- }
605
- // Add pagination status if pagination is enabled
606
- if (pagination) {
607
- gridOptions.suppressPaginationPanel = true;
602
+ }
603
+ }
604
+ addDatasourcePanels(statusPanels) {
605
+ if (this.gridProDatasource) {
606
+ const datasourceStatusPanels = this.gridProDatasource.getDatasourceStatusBarPanels(this.isServerSide);
607
+ // Filter out duplicate datasource panels
608
+ const newDatasourcePanels = datasourceStatusPanels.filter((panel) => !this.panelExists(panel.statusPanel, statusPanels));
609
+ statusPanels.push(...newDatasourcePanels);
610
+ }
611
+ }
612
+ addPaginationPanel(statusPanels) {
613
+ if (this['_pagination']) {
614
+ if (!this.panelExists(GridProStatusBarTypes.pagination, statusPanels)) {
608
615
  statusPanels.push({
609
616
  statusPanel: GridProStatusBarTypes.pagination,
610
617
  align: 'right',
611
618
  });
612
619
  }
613
- // Store user-defined status panels for later
614
- const userDefinedStatusPanels = ((_a = gridOptions.statusBar) === null || _a === void 0 ? void 0 : _a.statusPanels) || [];
620
+ }
621
+ }
622
+ setupPaginationAndStatusBar(gridOptions) {
623
+ // Keep the guard for production code, but allow override for testing
624
+ // if (this.initialised && !allowDuplicateCheck) return;
625
+ var _a, _b;
626
+ if (this['_pagination']) {
627
+ gridOptions.pagination = true;
628
+ gridOptions.suppressScrollOnNewData = true;
629
+ gridOptions.paginationPageSize = this.paginationPageSize;
630
+ }
631
+ // Set default status bar with components based on configuration
632
+ if (this.withStatusBar && this.statusBarConfig) {
633
+ // For duplicate checking (testing scenario), clear existing status panels to prevent duplicates
634
+ // unless they are user-defined panels that we want to preserve
635
+ const existingPanels = ((_a = gridOptions.statusBar) === null || _a === void 0 ? void 0 : _a.statusPanels) || [];
636
+ const statusPanels = [...existingPanels];
637
+ this.addRowCountPanel(statusPanels);
638
+ this.addMaxRowsPanel(statusPanels);
639
+ this.addAggregationPanel(statusPanels);
640
+ this.addDatasourcePanels(statusPanels);
641
+ if (this['_pagination']) {
642
+ gridOptions.suppressPaginationPanel = true;
643
+ this.addPaginationPanel(statusPanels);
644
+ }
645
+ const userDefinedStatusPanels = ((_b = gridOptions.statusBar) === null || _b === void 0 ? void 0 : _b.statusPanels) || [];
646
+ const newUserDefinedPanels = userDefinedStatusPanels.filter((panel) => !this.panelExists(panel.statusPanel, statusPanels));
647
+ statusPanels.push(...newUserDefinedPanels);
615
648
  gridOptions.statusBar = {
616
- statusPanels: [...statusPanels, ...userDefinedStatusPanels],
649
+ statusPanels: statusPanels,
617
650
  };
618
651
  }
619
652
  }
@@ -77,4 +77,19 @@ export const foundationGridProStyles = css `
77
77
  padding-top: var(--ag-grid-size);
78
78
  padding-bottom: var(--ag-grid-size);
79
79
  }
80
+
81
+ /* Custom styling for the unsort icon (sortNone) - only for sortable columns */
82
+ .ag-header-cell.ag-header-cell-sortable .ag-icon-none {
83
+ opacity: 100%;
84
+ color: var(--rapid-ag-header-foreground-color, var(--ag-foreground-color, #fff));
85
+ font-size: calc(var(--ag-icon-size, 12px) * 1.2);
86
+ font-weight: bold;
87
+ }
88
+
89
+ .ag-header-cell.ag-header-cell-sortable:hover .ag-icon-none {
90
+ opacity: 100%;
91
+ color: var(--rapid-ag-header-foreground-color, var(--ag-foreground-color, #fff));
92
+ transform: scale(1.1);
93
+ transition: transform 0.2s ease;
94
+ }
80
95
  `;
@@ -6,24 +6,26 @@
6
6
  export class PaginationStatusBarComponent {
7
7
  init(params) {
8
8
  this.api = params.api;
9
- this.eGui = document.createElement('div');
10
- this.eGui.className = 'ag-status-panel';
11
- this.eGui.setAttribute('data-test-id', 'grid-pagination-panel');
12
- this.eGui.setAttribute('role', 'navigation');
13
- this.eGui.setAttribute('aria-label', 'Pagination controls');
14
- this.eGui.style.display = 'flex';
15
- this.eGui.style.alignItems = 'center';
16
- this.eGui.style.justifyContent = 'flex-end';
17
- this.eGui.style.height = 'var(--ag-status-bar-height)';
9
+ this.element = document.createElement('div');
10
+ this.element.className = 'ag-status-panel';
11
+ this.element.setAttribute('data-test-id', 'grid-pagination-panel');
12
+ this.element.setAttribute('role', 'navigation');
13
+ this.element.setAttribute('aria-label', 'Pagination controls');
14
+ this.element.style.display = 'flex';
15
+ this.element.style.alignItems = 'center';
16
+ this.element.style.justifyContent = 'flex-end';
17
+ this.element.style.height = 'var(--ag-status-bar-height)';
18
18
  this.createPaginationPanel();
19
19
  // Initial update
20
20
  this.updateDisplay();
21
21
  // Listen to pagination changes
22
- this.paginationChangedHandler = this.updateDisplay.bind(this);
22
+ this.paginationChangedHandler = () => {
23
+ this.updateDisplay();
24
+ };
23
25
  this.api.addEventListener('paginationChanged', this.paginationChangedHandler);
24
26
  }
25
27
  getGui() {
26
- return this.eGui;
28
+ return this.element;
27
29
  }
28
30
  createPaginationPanel() {
29
31
  // Create the main pagination panel
@@ -96,7 +98,7 @@ export class PaginationStatusBarComponent {
96
98
  this.paginationPanel.appendChild(this.nextPageButton);
97
99
  this.paginationPanel.appendChild(this.lastPageButton);
98
100
  // Add to main container
99
- this.eGui.appendChild(this.paginationPanel);
101
+ this.element.appendChild(this.paginationPanel);
100
102
  // Add event listeners with keyboard support
101
103
  this.addPaginationEventListeners();
102
104
  }
@@ -202,5 +204,7 @@ export class PaginationStatusBarComponent {
202
204
  if (this.api && this.paginationChangedHandler) {
203
205
  this.api.removeEventListener('paginationChanged', this.paginationChangedHandler);
204
206
  }
207
+ this.api = null;
208
+ this.paginationChangedHandler = null;
205
209
  }
206
210
  }