@progress/kendo-angular-grid 21.2.0 → 21.3.0-develop.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/common/id.service.d.ts +2 -0
- package/esm2022/column-menu/column-menu-chooser.component.mjs +1 -2
- package/esm2022/column-menu/column-menu.component.mjs +4 -6
- package/esm2022/columns/column-base.mjs +1 -2
- package/esm2022/common/id.service.mjs +4 -0
- package/esm2022/grid.component.mjs +325 -274
- package/esm2022/package-metadata.mjs +2 -2
- package/esm2022/rendering/header/header.component.mjs +4 -4
- package/esm2022/rendering/toolbar/tools/ai-assistant/ai-assistant.component.mjs +11 -588
- package/esm2022/rendering/toolbar/tools/ai-assistant/ai-request-response.service.mjs +624 -0
- package/fesm2022/progress-kendo-angular-grid.mjs +1456 -1370
- package/grid.component.d.ts +41 -1
- package/package.json +25 -25
- package/rendering/toolbar/tools/ai-assistant/ai-assistant.component.d.ts +3 -23
- package/rendering/toolbar/tools/ai-assistant/ai-request-response.service.d.ts +61 -0
- package/schematics/ngAdd/index.js +7 -7
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
/**-----------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright © 2025 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, NgZone } from '@angular/core';
|
|
6
|
+
import { isPresent } from '@progress/kendo-angular-common';
|
|
7
|
+
import { ColumnInfoService } from './../../../../common/column-info.service';
|
|
8
|
+
import { ContextService } from './../../../../common/provider.service';
|
|
9
|
+
import { convertDateStringsInFilter, highlightBy } from './utils';
|
|
10
|
+
import { isCheckboxColumn } from '../../../../columns/column-base';
|
|
11
|
+
import { CommandColumnComponent } from '../../../../columns/command-column.component';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
import * as i1 from "./../../../../common/provider.service";
|
|
14
|
+
import * as i2 from "./../../../../common/column-info.service";
|
|
15
|
+
/**
|
|
16
|
+
* @hidden
|
|
17
|
+
*
|
|
18
|
+
* Service that builds AI requests and processes AI responses for the Grid.
|
|
19
|
+
* Used internally by both the Grid component and the AI Assistant tool.
|
|
20
|
+
*/
|
|
21
|
+
export class GridAIRequestResponseService {
|
|
22
|
+
ctx;
|
|
23
|
+
columnInfoService;
|
|
24
|
+
zone;
|
|
25
|
+
constructor(ctx, columnInfoService, zone) {
|
|
26
|
+
this.ctx = ctx;
|
|
27
|
+
this.columnInfoService = columnInfoService;
|
|
28
|
+
this.zone = zone;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Builds the request body for the AI service based on the Grid's column structure.
|
|
32
|
+
* Returns a column descriptor tree that includes column metadata for the AI service.
|
|
33
|
+
*/
|
|
34
|
+
buildRequestBody(promptMessage, role) {
|
|
35
|
+
const columnsTree = this.buildColumnDescriptors();
|
|
36
|
+
return {
|
|
37
|
+
role: role || 'user',
|
|
38
|
+
contents: [
|
|
39
|
+
{
|
|
40
|
+
text: promptMessage
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
columns: columnsTree
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Builds a nested column descriptor tree based on the Grid's column structure.
|
|
48
|
+
* Includes root columns and their nested children (for ColumnGroup and SpanColumn).
|
|
49
|
+
*/
|
|
50
|
+
buildColumnDescriptors() {
|
|
51
|
+
const rootColumns = this.ctx?.grid?.columnList?.rootColumns() || [];
|
|
52
|
+
const buildDescriptor = (col) => {
|
|
53
|
+
const hasChildren = Boolean(col.hasChildren && col.childrenArray?.length);
|
|
54
|
+
const descriptor = {
|
|
55
|
+
id: col.id,
|
|
56
|
+
field: col.field,
|
|
57
|
+
header: col.title
|
|
58
|
+
};
|
|
59
|
+
if (hasChildren) {
|
|
60
|
+
descriptor.columns = col.childrenArray.map((c) => buildDescriptor(c));
|
|
61
|
+
}
|
|
62
|
+
// For special columns that don't have a field, emit an optional type token
|
|
63
|
+
// so the AI service knows how to treat them (checkbox/command/reorder)
|
|
64
|
+
if (!col.field) {
|
|
65
|
+
if (isCheckboxColumn(col)) {
|
|
66
|
+
descriptor.type = 'checkbox';
|
|
67
|
+
}
|
|
68
|
+
else if (col instanceof CommandColumnComponent) {
|
|
69
|
+
descriptor.type = 'command';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return descriptor;
|
|
73
|
+
};
|
|
74
|
+
return rootColumns.map((col) => buildDescriptor(col));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Processes AI response commands and applies them to the Grid.
|
|
78
|
+
* Returns an array of display messages for each command.
|
|
79
|
+
*/
|
|
80
|
+
processCommands(commands, columns, leafColumns) {
|
|
81
|
+
const messages = [];
|
|
82
|
+
this.executeCommands(commands || [], columns, leafColumns, messages);
|
|
83
|
+
return messages;
|
|
84
|
+
}
|
|
85
|
+
executeCommands(commands, columns, leafColumns, messages) {
|
|
86
|
+
if (!commands?.length) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const grid = this.ctx.grid;
|
|
90
|
+
const isFilterable = Boolean(grid.filterable);
|
|
91
|
+
const isSortable = Boolean(grid.sortable);
|
|
92
|
+
const isGroupable = Boolean(grid.groupable);
|
|
93
|
+
const findColumnById = (id) => grid.columnList.toArray().find((c) => c.id === id);
|
|
94
|
+
const updateColumnHierarchy = (column, updater) => {
|
|
95
|
+
const changed = [];
|
|
96
|
+
const queue = [column];
|
|
97
|
+
while (queue.length) {
|
|
98
|
+
const current = queue.shift();
|
|
99
|
+
if (!current) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const didChange = updater(current);
|
|
103
|
+
if (didChange) {
|
|
104
|
+
changed.push(current);
|
|
105
|
+
}
|
|
106
|
+
if (current.hasChildren && current.childrenArray?.length) {
|
|
107
|
+
queue.push(...current.childrenArray);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return changed;
|
|
111
|
+
};
|
|
112
|
+
commands.forEach((cmd) => {
|
|
113
|
+
let displayMessage = cmd.message || '';
|
|
114
|
+
if (this.isColumnCommand(cmd.type)) {
|
|
115
|
+
if (cmd.id) {
|
|
116
|
+
const column = findColumnById(cmd.id);
|
|
117
|
+
const replacement = this.getColumnReplacement(column);
|
|
118
|
+
displayMessage = this.replaceQuotedColumnId(displayMessage, replacement);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
messages.push(displayMessage);
|
|
122
|
+
switch (cmd.type) {
|
|
123
|
+
case 'GridSort':
|
|
124
|
+
if (!isSortable) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
this.processArrayResponse([cmd.sort], grid.currentState.sort || [], (item) => item.field, (mergedArray) => grid.sortChange.next(mergedArray));
|
|
128
|
+
break;
|
|
129
|
+
case 'GridClearSort':
|
|
130
|
+
if (!isSortable) {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
grid.sortChange.next([]);
|
|
134
|
+
break;
|
|
135
|
+
case 'GridFilter':
|
|
136
|
+
if (!isFilterable) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
this.processFilterResponse(cmd.filter);
|
|
140
|
+
break;
|
|
141
|
+
case 'GridClearFilter':
|
|
142
|
+
if (!isFilterable) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
grid.filterChange.next(undefined);
|
|
146
|
+
break;
|
|
147
|
+
case 'GridGroup':
|
|
148
|
+
if (!isGroupable) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
this.processArrayResponse([cmd.group], grid.currentState.group || [], (item) => item.field, (mergedArray) => grid.groupChange.next(mergedArray));
|
|
152
|
+
break;
|
|
153
|
+
case 'GridClearGroup':
|
|
154
|
+
if (!isGroupable) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
grid.groupChange.next([]);
|
|
158
|
+
break;
|
|
159
|
+
case 'GridHighlight':
|
|
160
|
+
if (!this.ctx.highlightDirective) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
this.processHighlightResponse([cmd.highlight], columns);
|
|
164
|
+
break;
|
|
165
|
+
case 'GridClearHighlight':
|
|
166
|
+
if (!this.ctx.highlightDirective) {
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
this.ctx.highlightDirective['setState']([]);
|
|
170
|
+
break;
|
|
171
|
+
case 'GridSelect': {
|
|
172
|
+
this.processSelectionResponse([cmd.select], columns, leafColumns, messages);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case 'GridClearSelect': {
|
|
176
|
+
const selectionInstance = this.getSelectionInstance();
|
|
177
|
+
if (!selectionInstance) {
|
|
178
|
+
this.updateLastMessage(messages, this.ctx.localization?.get('aiAssistantSelectionNotEnabled'));
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
this.applySelectionState(selectionInstance, []);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case 'GridColumnResize': {
|
|
185
|
+
const col = findColumnById(cmd.id);
|
|
186
|
+
if (!col) {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
let newWidth;
|
|
190
|
+
if (typeof cmd.size === 'number') {
|
|
191
|
+
newWidth = cmd.size;
|
|
192
|
+
}
|
|
193
|
+
else if (typeof cmd.size === 'string') {
|
|
194
|
+
const numericPart = parseFloat(cmd.size);
|
|
195
|
+
if (!isNaN(numericPart)) {
|
|
196
|
+
newWidth = numericPart;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (typeof newWidth === 'number') {
|
|
200
|
+
const oldWidth = col.width;
|
|
201
|
+
col.width = newWidth;
|
|
202
|
+
const args = [{ column: col, oldWidth: oldWidth, newWidth: newWidth }];
|
|
203
|
+
grid.columnResize.emit(args);
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 'GridColumnReorder': {
|
|
208
|
+
const col = findColumnById(cmd.id);
|
|
209
|
+
if (!col) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
const newPosition = Number(cmd.position);
|
|
213
|
+
if (!isNaN(newPosition) && newPosition >= 0) {
|
|
214
|
+
this.changeColumnPosition(col, newPosition);
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case 'GridColumnShow':
|
|
219
|
+
case 'GridColumnHide': {
|
|
220
|
+
const col = findColumnById(cmd.id);
|
|
221
|
+
if (!col) {
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
const targetHidden = cmd.type === 'GridColumnHide';
|
|
225
|
+
const changed = updateColumnHierarchy(col, (current) => {
|
|
226
|
+
if (current.hidden === targetHidden) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
current.hidden = targetHidden;
|
|
230
|
+
return true;
|
|
231
|
+
});
|
|
232
|
+
if (changed.length) {
|
|
233
|
+
this.columnInfoService.changeVisibility(changed);
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case 'GridColumnLock':
|
|
238
|
+
case 'GridColumnUnlock': {
|
|
239
|
+
const col = findColumnById(cmd.id);
|
|
240
|
+
if (!col) {
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
const targetLocked = cmd.type === 'GridColumnLock';
|
|
244
|
+
const changed = updateColumnHierarchy(col, (current) => {
|
|
245
|
+
if (current.locked === targetLocked) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
current.locked = targetLocked;
|
|
249
|
+
return true;
|
|
250
|
+
});
|
|
251
|
+
if (changed.length) {
|
|
252
|
+
this.columnInfoService.changeLocked(changed);
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
case 'GridPage': {
|
|
257
|
+
this.processPageCommand(cmd);
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
case 'GridPageSize': {
|
|
261
|
+
this.processPageSizeCommand(cmd);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case 'GridExportExcel': {
|
|
265
|
+
this.runExportWithFileName(this.ctx.excelComponent, cmd.fileName, () => grid.saveAsExcel());
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 'GridExportPDF': {
|
|
269
|
+
this.runExportWithFileName(this.ctx.pdfComponent, cmd.fileName, () => grid.emitPDFExportEvent());
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
default:
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
processArrayResponse(newItems, currentItems, getField, updateGrid) {
|
|
278
|
+
if (newItems?.length === 0) {
|
|
279
|
+
updateGrid([]);
|
|
280
|
+
}
|
|
281
|
+
else if (newItems?.length) {
|
|
282
|
+
let mergedArray = [...newItems];
|
|
283
|
+
const newFields = newItems.map(getField);
|
|
284
|
+
const existingItemsToKeep = currentItems.filter(item => !newFields.includes(getField(item)));
|
|
285
|
+
mergedArray = [...mergedArray, ...existingItemsToKeep];
|
|
286
|
+
updateGrid(mergedArray);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
runExportWithFileName(component, fileName, action) {
|
|
290
|
+
if (!component || !fileName) {
|
|
291
|
+
action();
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const previousFileName = component.fileName;
|
|
295
|
+
component.fileName = fileName;
|
|
296
|
+
action();
|
|
297
|
+
const isExcel = component === this.ctx.excelComponent;
|
|
298
|
+
if (isExcel) {
|
|
299
|
+
this.zone.runOutsideAngular(() => {
|
|
300
|
+
this.ctx.excelComponent.fileCreated.subscribe(() => {
|
|
301
|
+
component.fileName = previousFileName;
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
component.fileName = previousFileName;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
processPageCommand(command) {
|
|
310
|
+
const pageSize = this.getCurrentPageSizeValue();
|
|
311
|
+
if (!isPresent(pageSize) || pageSize <= 0) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const total = this.getTotalItemsCount();
|
|
315
|
+
const requestedPage = Number(command.page);
|
|
316
|
+
let targetPage = Number.isFinite(requestedPage) ? Math.floor(requestedPage) : 1;
|
|
317
|
+
if (targetPage < 1) {
|
|
318
|
+
targetPage = 1;
|
|
319
|
+
}
|
|
320
|
+
if (isPresent(total) && pageSize > 0) {
|
|
321
|
+
const maxPage = Math.max(1, Math.ceil(total / pageSize));
|
|
322
|
+
targetPage = Math.min(targetPage, maxPage);
|
|
323
|
+
}
|
|
324
|
+
const skip = (targetPage - 1) * pageSize;
|
|
325
|
+
this.emitGridPageChange(skip, pageSize);
|
|
326
|
+
}
|
|
327
|
+
processPageSizeCommand(command) {
|
|
328
|
+
const rawPageSize = Number(command.pageSize);
|
|
329
|
+
if (!Number.isFinite(rawPageSize)) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const newPageSize = Math.max(1, Math.floor(rawPageSize));
|
|
333
|
+
const skip = Math.max(0, this.ctx.grid?.skip ?? 0);
|
|
334
|
+
this.ensurePageSizeOption(newPageSize);
|
|
335
|
+
this.emitGridPageChange(skip, newPageSize);
|
|
336
|
+
}
|
|
337
|
+
emitGridPageChange(skip, take) {
|
|
338
|
+
const grid = this.ctx.grid;
|
|
339
|
+
const normalizedSkip = Math.max(0, Math.floor(skip));
|
|
340
|
+
const normalizedTake = Math.max(1, Math.floor(take));
|
|
341
|
+
grid.skip = normalizedSkip;
|
|
342
|
+
grid.pageSize = normalizedTake;
|
|
343
|
+
grid.pageChange.emit({ skip: normalizedSkip, take: normalizedTake });
|
|
344
|
+
}
|
|
345
|
+
ensurePageSizeOption(pageSize) {
|
|
346
|
+
const grid = this.ctx.grid;
|
|
347
|
+
if (!grid) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const pageable = grid.pageable;
|
|
351
|
+
if (!pageable || typeof pageable === 'boolean') {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const pageSizes = pageable.pageSizes;
|
|
355
|
+
if (!Array.isArray(pageSizes) || pageSizes.length === 0) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (pageSizes.includes(pageSize)) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const uniqueSizes = [pageSize, ...pageSizes.filter(size => size !== pageSize)];
|
|
362
|
+
grid.pageable = {
|
|
363
|
+
...pageable,
|
|
364
|
+
pageSizes: uniqueSizes
|
|
365
|
+
};
|
|
366
|
+
const changeDetector = grid?.changeDetectorRef;
|
|
367
|
+
if (changeDetector && typeof changeDetector.markForCheck === 'function') {
|
|
368
|
+
changeDetector.markForCheck();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
getCurrentPageSizeValue() {
|
|
372
|
+
const grid = this.ctx.grid;
|
|
373
|
+
if (!grid) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
const candidates = [grid.pageSize, grid.currentState?.take, this.ctx.dataBindingDirective?.['state']?.take];
|
|
377
|
+
for (const candidate of candidates) {
|
|
378
|
+
if (typeof candidate === 'number' && candidate > 0) {
|
|
379
|
+
return candidate;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const pageable = grid.pageable;
|
|
383
|
+
if (pageable && typeof pageable === 'object' && Array.isArray(pageable.pageSizes)) {
|
|
384
|
+
const numericSize = pageable.pageSizes.find(size => typeof size === 'number' && size > 0);
|
|
385
|
+
if (numericSize) {
|
|
386
|
+
return numericSize;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const originalData = this.ctx.dataBindingDirective?.['originalData'];
|
|
390
|
+
if (Array.isArray(originalData) && originalData.length > 0) {
|
|
391
|
+
return originalData.length;
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
getTotalItemsCount() {
|
|
396
|
+
const grid = this.ctx.grid;
|
|
397
|
+
if (!grid) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const gridData = grid.data;
|
|
401
|
+
if (gridData && typeof gridData.total === 'number') {
|
|
402
|
+
return gridData.total;
|
|
403
|
+
}
|
|
404
|
+
const view = grid.view;
|
|
405
|
+
if (view && typeof view.total === 'number') {
|
|
406
|
+
return view.total;
|
|
407
|
+
}
|
|
408
|
+
const originalData = this.ctx.dataBindingDirective?.['originalData'];
|
|
409
|
+
if (Array.isArray(originalData)) {
|
|
410
|
+
return originalData.length;
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
getSelectionInstance() {
|
|
415
|
+
const selectionDirective = this.ctx.grid?.selectionDirective;
|
|
416
|
+
if (selectionDirective && typeof selectionDirective === 'object') {
|
|
417
|
+
return selectionDirective;
|
|
418
|
+
}
|
|
419
|
+
const defaultSelection = this.ctx.grid?.defaultSelection;
|
|
420
|
+
if (defaultSelection && typeof defaultSelection === 'object') {
|
|
421
|
+
return defaultSelection;
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
updateLastMessage(messages, newMessage) {
|
|
426
|
+
if (!messages.length) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
messages[messages.length - 1] = newMessage;
|
|
430
|
+
}
|
|
431
|
+
isColumnCommand(type) {
|
|
432
|
+
return type === 'GridColumnResize' ||
|
|
433
|
+
type === 'GridColumnReorder' ||
|
|
434
|
+
type === 'GridColumnShow' ||
|
|
435
|
+
type === 'GridColumnHide' ||
|
|
436
|
+
type === 'GridColumnLock' ||
|
|
437
|
+
type === 'GridColumnUnlock';
|
|
438
|
+
}
|
|
439
|
+
getColumnReplacement(column) {
|
|
440
|
+
if (!column) {
|
|
441
|
+
return '';
|
|
442
|
+
}
|
|
443
|
+
if (column.title && String(column.title).trim()) {
|
|
444
|
+
return String(column.title).trim();
|
|
445
|
+
}
|
|
446
|
+
if (column.field && String(column.field).trim()) {
|
|
447
|
+
return String(column.field).trim();
|
|
448
|
+
}
|
|
449
|
+
return '';
|
|
450
|
+
}
|
|
451
|
+
replaceQuotedColumnId(message, replacement) {
|
|
452
|
+
if (!replacement) {
|
|
453
|
+
const columnIdPattern = /(?:"|")(k-grid\d+-col\d+)(?:"|")\s*/g;
|
|
454
|
+
return message.replace(columnIdPattern, '').replace(/\s{2,}/g, ' ').trim();
|
|
455
|
+
}
|
|
456
|
+
const columnIdPattern = /(?:"|")(k-grid\d+-col\d+)(?:"|")/g;
|
|
457
|
+
return message.replace(columnIdPattern, (match) => {
|
|
458
|
+
const isEncoded = match.startsWith('"');
|
|
459
|
+
return isEncoded ? `"${replacement}"` : `"${replacement}"`;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
getHighlightItems(descriptors, columns) {
|
|
463
|
+
if (!descriptors?.length) {
|
|
464
|
+
return [];
|
|
465
|
+
}
|
|
466
|
+
const data = this.ctx.dataBindingDirective?.['originalData'] || [];
|
|
467
|
+
return highlightBy(data, descriptors, columns);
|
|
468
|
+
}
|
|
469
|
+
processSelectionResponse(selection, columns, leafColumns, messages) {
|
|
470
|
+
const selectionInstance = this.getSelectionInstance();
|
|
471
|
+
if (!selectionInstance) {
|
|
472
|
+
this.updateLastMessage(messages, this.ctx.localization?.get('aiAssistantSelectionNotEnabled'));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const descriptors = (selection || []).filter((descriptor) => Boolean(descriptor));
|
|
476
|
+
if (descriptors.length === 0) {
|
|
477
|
+
this.applySelectionState(selectionInstance, []);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const highlightItems = this.getHighlightItems(descriptors, columns);
|
|
481
|
+
if (!highlightItems.length) {
|
|
482
|
+
this.applySelectionState(selectionInstance, []);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const hasCellSelections = highlightItems.some(item => isPresent(item.columnKey));
|
|
486
|
+
const hasRowSelections = highlightItems.some(item => !isPresent(item.columnKey));
|
|
487
|
+
const isCellMode = selectionInstance.isCellSelectionMode;
|
|
488
|
+
if ((!isCellMode && hasCellSelections) || (isCellMode && hasRowSelections)) {
|
|
489
|
+
const key = isCellMode ? 'aiAssistantSelectionRowModeRequired' : 'aiAssistantSelectionCellModeRequired';
|
|
490
|
+
this.updateLastMessage(messages, this.ctx.localization?.get(key));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const selectionState = this.mapHighlightItemsToSelection(selectionInstance, highlightItems, isCellMode, leafColumns);
|
|
494
|
+
this.applySelectionState(selectionInstance, selectionState);
|
|
495
|
+
}
|
|
496
|
+
mapHighlightItemsToSelection(selectionInstance, highlightItems, isCellMode, leafColumns) {
|
|
497
|
+
const data = this.ctx.dataBindingDirective?.['originalData'] || [];
|
|
498
|
+
if (isCellMode) {
|
|
499
|
+
const mapped = highlightItems
|
|
500
|
+
.filter(item => isPresent(item.itemKey) && isPresent(item.columnKey))
|
|
501
|
+
.map(item => {
|
|
502
|
+
const rowIndex = item.itemKey;
|
|
503
|
+
const columnIndex = item.columnKey;
|
|
504
|
+
const dataItem = data[rowIndex];
|
|
505
|
+
if (!isPresent(dataItem)) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
if (typeof selectionInstance['getSelectionItem'] === 'function') {
|
|
509
|
+
const columnComponent = leafColumns[columnIndex];
|
|
510
|
+
const selectionItem = selectionInstance['getSelectionItem']({ dataItem, index: rowIndex }, columnComponent, columnIndex);
|
|
511
|
+
if (selectionItem && isPresent(selectionItem.itemKey) && isPresent(selectionItem.columnKey)) {
|
|
512
|
+
return selectionItem;
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
const itemKey = typeof selectionInstance.getItemKey === 'function'
|
|
517
|
+
? selectionInstance.getItemKey({ dataItem, index: rowIndex })
|
|
518
|
+
: rowIndex;
|
|
519
|
+
return isPresent(itemKey) ? { itemKey, columnKey: columnIndex } : null;
|
|
520
|
+
})
|
|
521
|
+
.filter((item) => isPresent(item));
|
|
522
|
+
return mapped.filter((item, index, self) => self.findIndex(other => other.itemKey === item.itemKey && other.columnKey === item.columnKey) === index);
|
|
523
|
+
}
|
|
524
|
+
const rowKeys = highlightItems
|
|
525
|
+
.filter(item => isPresent(item.itemKey))
|
|
526
|
+
.map(item => {
|
|
527
|
+
const rowIndex = item.itemKey;
|
|
528
|
+
const dataItem = data[rowIndex];
|
|
529
|
+
if (!isPresent(dataItem)) {
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
if (typeof selectionInstance.getItemKey === 'function') {
|
|
533
|
+
return selectionInstance.getItemKey({ dataItem, index: rowIndex });
|
|
534
|
+
}
|
|
535
|
+
return rowIndex;
|
|
536
|
+
})
|
|
537
|
+
.filter(isPresent);
|
|
538
|
+
return Array.from(new Set(rowKeys));
|
|
539
|
+
}
|
|
540
|
+
applySelectionState(selectionInstance, selectionState) {
|
|
541
|
+
selectionInstance.selectedKeys = selectionState;
|
|
542
|
+
if (typeof selectionInstance['setState'] === 'function') {
|
|
543
|
+
selectionInstance['setState'](selectionState);
|
|
544
|
+
}
|
|
545
|
+
const changeDetector = selectionInstance['cd'];
|
|
546
|
+
if (changeDetector && typeof changeDetector.markForCheck === 'function') {
|
|
547
|
+
changeDetector.markForCheck();
|
|
548
|
+
}
|
|
549
|
+
if (typeof selectionInstance['notifyChange'] === 'function') {
|
|
550
|
+
selectionInstance['notifyChange']();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
processHighlightResponse(highlight, columns) {
|
|
554
|
+
const highlightedItems = this.getHighlightItems(highlight, columns);
|
|
555
|
+
this.ctx.highlightDirective['setState'](highlightedItems);
|
|
556
|
+
}
|
|
557
|
+
processFilterResponse(filter) {
|
|
558
|
+
const processedFilter = convertDateStringsInFilter(filter);
|
|
559
|
+
const clearFilter = Object.keys(processedFilter).length === 0;
|
|
560
|
+
if (clearFilter) {
|
|
561
|
+
this.ctx.grid.filterChange.next(undefined);
|
|
562
|
+
}
|
|
563
|
+
else if (processedFilter?.filters.length) {
|
|
564
|
+
const currentFilter = this.ctx.grid.currentState.filter;
|
|
565
|
+
let mergedFilter = processedFilter;
|
|
566
|
+
if (currentFilter && currentFilter.filters?.length > 0) {
|
|
567
|
+
mergedFilter = {
|
|
568
|
+
logic: 'and',
|
|
569
|
+
filters: [
|
|
570
|
+
currentFilter,
|
|
571
|
+
processedFilter
|
|
572
|
+
]
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
this.ctx.grid.filterChange.next(mergedFilter);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
changeColumnPosition(column, newPosition) {
|
|
579
|
+
const grid = this.ctx.grid;
|
|
580
|
+
if (!grid?.columns) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const currentColumns = grid.columns.toArray();
|
|
584
|
+
const currentIndex = currentColumns.findIndex(col => col === column);
|
|
585
|
+
if (currentIndex === -1) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (newPosition < 0 || newPosition >= currentColumns.length) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const sortedColumns = currentColumns
|
|
592
|
+
.map((col, idx) => ({ col, physicalIndex: idx, visualOrder: col.orderIndex ?? idx }))
|
|
593
|
+
.sort((a, b) => a.visualOrder - b.visualOrder);
|
|
594
|
+
const currentVisualPos = sortedColumns.findIndex(item => item.physicalIndex === currentIndex);
|
|
595
|
+
if (currentVisualPos === newPosition) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
currentColumns.forEach((col, idx) => {
|
|
599
|
+
const sortedIndex = sortedColumns.findIndex(item => item.physicalIndex === idx);
|
|
600
|
+
if (idx === currentIndex) {
|
|
601
|
+
col.orderIndex = newPosition;
|
|
602
|
+
}
|
|
603
|
+
else if (currentVisualPos < newPosition) {
|
|
604
|
+
col.orderIndex = (sortedIndex > currentVisualPos && sortedIndex <= newPosition)
|
|
605
|
+
? sortedIndex - 1 : sortedIndex;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
col.orderIndex = (sortedIndex >= newPosition && sortedIndex < currentVisualPos)
|
|
609
|
+
? sortedIndex + 1 : sortedIndex;
|
|
610
|
+
}
|
|
611
|
+
col.isReordered = true;
|
|
612
|
+
});
|
|
613
|
+
grid.columnReorder.emit({
|
|
614
|
+
column: column,
|
|
615
|
+
oldIndex: currentVisualPos,
|
|
616
|
+
newIndex: newPosition
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GridAIRequestResponseService, deps: [{ token: i1.ContextService }, { token: i2.ColumnInfoService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
620
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GridAIRequestResponseService });
|
|
621
|
+
}
|
|
622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GridAIRequestResponseService, decorators: [{
|
|
623
|
+
type: Injectable
|
|
624
|
+
}], ctorParameters: () => [{ type: i1.ContextService }, { type: i2.ColumnInfoService }, { type: i0.NgZone }] });
|