@progress/kendo-angular-grid 21.0.0-develop.9 → 21.0.1-develop.1
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/codemods/utils.js +485 -327
- package/codemods/v20/grid-kendogridgroupbinding.js +6 -6
- package/codemods/v21/grid-gridtoolbaraiopenevent.js +14 -0
- package/codemods/v21/grid-gridtoolbaraipromptrequestevent.js +14 -0
- package/codemods/v21/grid-gridtoolbaraipromptsettings.js +14 -0
- package/codemods/v21/grid-gridtoolbarairequestoptions.js +14 -0
- package/codemods/v21/grid-gridtoolbarairesponseerrorevent.js +14 -0
- package/codemods/v21/grid-gridtoolbaraiwindowsettings.js +14 -0
- package/column-menu/column-chooser.component.d.ts +0 -1
- package/common/id.service.d.ts +1 -0
- package/common/provider.service.d.ts +4 -0
- package/esm2022/column-menu/column-chooser.component.mjs +2 -7
- package/esm2022/columns/column-base.mjs +1 -1
- package/esm2022/common/id.service.mjs +3 -0
- package/esm2022/common/provider.service.mjs +2 -0
- package/esm2022/dragdrop/drag-hint.service.mjs +6 -2
- package/esm2022/excel/excel.component.mjs +13 -2
- package/esm2022/grid.component.mjs +18 -0
- package/esm2022/localization/messages.mjs +19 -1
- package/esm2022/navigation/navigation.service.mjs +47 -1
- package/esm2022/package-metadata.mjs +2 -2
- package/esm2022/pdf/pdf.component.mjs +6 -0
- package/esm2022/rendering/list.component.mjs +16 -5
- package/esm2022/rendering/toolbar/tools/ai-assistant/ai-assistant.component.mjs +557 -29
- package/esm2022/rendering/toolbar/tools/ai-assistant/models.mjs +2 -2
- package/esm2022/row-reordering/row-reorder.service.mjs +28 -0
- package/excel/excel.component.d.ts +6 -2
- package/fesm2022/progress-kendo-angular-grid.mjs +717 -49
- package/localization/messages.d.ts +13 -1
- package/navigation/navigation.service.d.ts +1 -0
- package/package.json +58 -25
- package/rendering/toolbar/tools/ai-assistant/ai-assistant.component.d.ts +26 -5
- package/rendering/toolbar/tools/ai-assistant/ai-tool.directive.d.ts +8 -8
- package/rendering/toolbar/tools/ai-assistant/models.d.ts +65 -33
- package/row-reordering/row-reorder.service.d.ts +10 -1
- package/schematics/ngAdd/index.js +7 -7
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
* Copyright © 2025 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, ViewChild } from '@angular/core';
|
|
5
|
+
import { Component, ViewChild, NgZone } from '@angular/core';
|
|
6
6
|
import { AIPromptComponent, OutputViewComponent, PromptViewComponent, AIPromptCustomMessagesComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective } from '@progress/kendo-angular-conversational-ui';
|
|
7
7
|
import { HttpClient, HttpRequest } from '@angular/common/http';
|
|
8
8
|
import { ContextService } from './../../../../common/provider.service';
|
|
9
9
|
import { ColumnInfoService } from './../../../../common/column-info.service';
|
|
10
|
-
import {
|
|
10
|
+
import { GridAIAssistantResponseSuccessEvent, GridAIAssistantResponseErrorEvent } from './models';
|
|
11
11
|
import { NgIf } from '@angular/common';
|
|
12
12
|
import { convertDateStringsInFilter, highlightBy } from './utils';
|
|
13
|
+
import { isCheckboxColumn } from '../../../../columns/column-base';
|
|
14
|
+
import { CommandColumnComponent } from '../../../../columns/command-column.component';
|
|
15
|
+
import { isPresent } from '@progress/kendo-angular-common';
|
|
13
16
|
import * as i0 from "@angular/core";
|
|
14
17
|
import * as i1 from "@angular/common/http";
|
|
15
18
|
import * as i2 from "./../../../../common/provider.service";
|
|
@@ -21,6 +24,7 @@ export class AiAssistantComponent {
|
|
|
21
24
|
http;
|
|
22
25
|
ctx;
|
|
23
26
|
columnInfoService;
|
|
27
|
+
zone;
|
|
24
28
|
aiPrompt;
|
|
25
29
|
activeView = 0;
|
|
26
30
|
requestUrl;
|
|
@@ -34,15 +38,49 @@ export class AiAssistantComponent {
|
|
|
34
38
|
currentRequestSubscription = null;
|
|
35
39
|
//Remove this when the AI Assistant has a built-in loading indicator
|
|
36
40
|
loadingOutput = { id: 'k-loading-item', output: '', prompt: '' };
|
|
41
|
+
// flat columns used for highlight utilities (expects { field })
|
|
37
42
|
columns = [];
|
|
43
|
+
leafColumns = [];
|
|
44
|
+
// nested tree representing the actual Grid column components sent to the AI service
|
|
45
|
+
columnsTree = [];
|
|
38
46
|
idCounter = 0;
|
|
39
|
-
constructor(http, ctx, columnInfoService) {
|
|
47
|
+
constructor(http, ctx, columnInfoService, zone) {
|
|
40
48
|
this.http = http;
|
|
41
49
|
this.ctx = ctx;
|
|
42
50
|
this.columnInfoService = columnInfoService;
|
|
51
|
+
this.zone = zone;
|
|
43
52
|
}
|
|
44
53
|
ngAfterViewInit() {
|
|
45
|
-
|
|
54
|
+
// Build a nested GridColumnDescriptor tree based on the actual Grid columns structure.
|
|
55
|
+
// This includes root columns and their nested children (for ColumnGroup and SpanColumn).
|
|
56
|
+
const rootColumns = this.ctx?.grid?.columnList?.rootColumns() || [];
|
|
57
|
+
const buildDescriptor = (col) => {
|
|
58
|
+
const hasChildren = Boolean(col.hasChildren && col.childrenArray?.length);
|
|
59
|
+
const descriptor = {
|
|
60
|
+
id: col.id,
|
|
61
|
+
field: col.field
|
|
62
|
+
};
|
|
63
|
+
if (hasChildren) {
|
|
64
|
+
descriptor.header = col.displayTitle;
|
|
65
|
+
descriptor.columns = col.childrenArray.map((c) => buildDescriptor(c));
|
|
66
|
+
}
|
|
67
|
+
// For special columns that don't have a field, emit an optional type token
|
|
68
|
+
// so the AI service knows how to treat them (checkbox/command/reorder)
|
|
69
|
+
if (!col.field) {
|
|
70
|
+
if (isCheckboxColumn(col)) {
|
|
71
|
+
descriptor.type = 'checkbox';
|
|
72
|
+
}
|
|
73
|
+
else if (col instanceof CommandColumnComponent) {
|
|
74
|
+
descriptor.type = 'command';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return descriptor;
|
|
78
|
+
};
|
|
79
|
+
this.columnsTree = rootColumns.map((col) => buildDescriptor(col));
|
|
80
|
+
// Preserve a flat columns array (fields) for highlight utilities.
|
|
81
|
+
// Use leafNamedColumns as the canonical list of leaf columns with display titles.
|
|
82
|
+
this.leafColumns = this.columnInfoService.leafNamedColumns || [];
|
|
83
|
+
this.columns = this.leafColumns.map((col) => ({ field: col.field }));
|
|
46
84
|
}
|
|
47
85
|
ngOnDestroy() {
|
|
48
86
|
this.unsubscribeCurrentRequest();
|
|
@@ -66,7 +104,8 @@ export class AiAssistantComponent {
|
|
|
66
104
|
this.lastMessage = ev.prompt;
|
|
67
105
|
}
|
|
68
106
|
this.requestData = {
|
|
69
|
-
|
|
107
|
+
// send nested tree to AI service
|
|
108
|
+
columns: this.columnsTree,
|
|
70
109
|
promptMessage: ev.prompt,
|
|
71
110
|
url: this.requestUrl,
|
|
72
111
|
requestOptions: {
|
|
@@ -110,30 +149,22 @@ export class AiAssistantComponent {
|
|
|
110
149
|
this.aiToolDirective.emitOpenClose = true;
|
|
111
150
|
this.aiToolDirective.toggleWindow();
|
|
112
151
|
}
|
|
113
|
-
const responseBody = response.body;
|
|
114
|
-
const responseSuccessEvent = new
|
|
152
|
+
const responseBody = response.body || { commands: [] };
|
|
153
|
+
const responseSuccessEvent = new GridAIAssistantResponseSuccessEvent(response);
|
|
115
154
|
this.aiToolDirective.responseSuccess.emit(responseSuccessEvent);
|
|
116
155
|
if (responseSuccessEvent.isDefaultPrevented()) {
|
|
117
156
|
this.deleteLoadingOutput();
|
|
118
157
|
return;
|
|
119
158
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.processFilterResponse(responseBody.filter);
|
|
125
|
-
}
|
|
126
|
-
if (isSortable && responseBody.sort) {
|
|
127
|
-
this.processArrayResponse(responseBody.sort, this.ctx.grid.currentState.sort || [], (item) => item.field, (mergedArray) => this.ctx.grid.sortChange.next(mergedArray));
|
|
128
|
-
}
|
|
129
|
-
if (isGroupable && responseBody.group) {
|
|
130
|
-
this.processArrayResponse(responseBody.group, this.ctx.grid.currentState.group || [], (item) => item.field, (mergedArray) => this.ctx.grid.groupChange.next(mergedArray));
|
|
131
|
-
}
|
|
132
|
-
if (this.ctx.highlightDirective && responseBody.highlight) {
|
|
133
|
-
this.processHighlightResponse(responseBody.highlight);
|
|
159
|
+
const messages = [];
|
|
160
|
+
// Include optional top-level message from the response
|
|
161
|
+
if (responseBody.message) {
|
|
162
|
+
messages.push(responseBody.message);
|
|
134
163
|
}
|
|
164
|
+
// Execute received commands sequentially and collect messages.
|
|
165
|
+
this.processCommands(responseBody.commands || [], messages);
|
|
135
166
|
const responseContentStart = [`${this.ctx.localization.get('aiAssistantOutputCardBodyContent')} \n`];
|
|
136
|
-
const responseContentBody =
|
|
167
|
+
const responseContentBody = messages
|
|
137
168
|
.map((output, idx) => `${idx + 1} ${output}`)
|
|
138
169
|
.join('\n');
|
|
139
170
|
const output = {
|
|
@@ -146,7 +177,7 @@ export class AiAssistantComponent {
|
|
|
146
177
|
this.aiToolDirective.promptOutputs.unshift(output);
|
|
147
178
|
}
|
|
148
179
|
handleError(error) {
|
|
149
|
-
const responseErrorEvent = new
|
|
180
|
+
const responseErrorEvent = new GridAIAssistantResponseErrorEvent(error);
|
|
150
181
|
this.aiToolDirective.responseError.emit(responseErrorEvent);
|
|
151
182
|
if (responseErrorEvent.isDefaultPrevented()) {
|
|
152
183
|
this.deleteLoadingOutput();
|
|
@@ -183,12 +214,468 @@ export class AiAssistantComponent {
|
|
|
183
214
|
updateGrid(mergedArray);
|
|
184
215
|
}
|
|
185
216
|
}
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
this.ctx.highlightDirective['setState']([]);
|
|
217
|
+
processCommands(commands, messages) {
|
|
218
|
+
if (!commands?.length) {
|
|
189
219
|
return;
|
|
190
220
|
}
|
|
191
|
-
const
|
|
221
|
+
const isFilterable = Boolean(this.ctx.grid.filterable);
|
|
222
|
+
const isSortable = Boolean(this.ctx.grid.sortable);
|
|
223
|
+
const isGroupable = Boolean(this.ctx.grid.groupable);
|
|
224
|
+
const findColumnById = (id) => this.ctx.grid.columnList.toArray().find((c) => c.id === id);
|
|
225
|
+
const updateColumnHierarchy = (column, updater) => {
|
|
226
|
+
const changed = [];
|
|
227
|
+
const queue = [column];
|
|
228
|
+
while (queue.length) {
|
|
229
|
+
const current = queue.shift();
|
|
230
|
+
if (!current) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const didChange = updater(current);
|
|
234
|
+
if (didChange) {
|
|
235
|
+
changed.push(current);
|
|
236
|
+
}
|
|
237
|
+
if (current.hasChildren && current.childrenArray?.length) {
|
|
238
|
+
queue.push(...current.childrenArray);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return changed;
|
|
242
|
+
};
|
|
243
|
+
commands.forEach((cmd) => {
|
|
244
|
+
let displayMessage = cmd.message || '';
|
|
245
|
+
if (this.isColumnCommand(cmd.type)) {
|
|
246
|
+
if (cmd.id) {
|
|
247
|
+
const column = findColumnById(cmd.id);
|
|
248
|
+
const replacement = this.getColumnReplacement(column);
|
|
249
|
+
displayMessage = this.replaceQuotedColumnId(displayMessage, replacement);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
messages.push(displayMessage);
|
|
253
|
+
switch (cmd.type) {
|
|
254
|
+
case 'GridSort':
|
|
255
|
+
if (!isSortable) {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
// cmd.sort is a SortDescriptor - replace or merge with existing sort
|
|
259
|
+
this.processArrayResponse([cmd.sort], this.ctx.grid.currentState.sort || [], (item) => item.field, (mergedArray) => this.ctx.grid.sortChange.next(mergedArray));
|
|
260
|
+
break;
|
|
261
|
+
case 'GridClearSort':
|
|
262
|
+
if (!isSortable) {
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
this.ctx.grid.sortChange.next([]);
|
|
266
|
+
break;
|
|
267
|
+
case 'GridFilter':
|
|
268
|
+
if (!isFilterable) {
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
this.processFilterResponse(cmd.filter);
|
|
272
|
+
break;
|
|
273
|
+
case 'GridClearFilter':
|
|
274
|
+
if (!isFilterable) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
this.ctx.grid.filterChange.next(undefined);
|
|
278
|
+
break;
|
|
279
|
+
case 'GridGroup':
|
|
280
|
+
if (!isGroupable) {
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
this.processArrayResponse([cmd.group], this.ctx.grid.currentState.group || [], (item) => item.field, (mergedArray) => this.ctx.grid.groupChange.next(mergedArray));
|
|
284
|
+
break;
|
|
285
|
+
case 'GridClearGroup':
|
|
286
|
+
if (!isGroupable) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
this.ctx.grid.groupChange.next([]);
|
|
290
|
+
break;
|
|
291
|
+
case 'GridHighlight':
|
|
292
|
+
if (!this.ctx.highlightDirective) {
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
this.processHighlightResponse([cmd.highlight]);
|
|
296
|
+
break;
|
|
297
|
+
case 'GridClearHighlight':
|
|
298
|
+
if (!this.ctx.highlightDirective) {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
this.ctx.highlightDirective['setState']([]);
|
|
302
|
+
break;
|
|
303
|
+
case 'GridSelect': {
|
|
304
|
+
this.processSelectionResponse([cmd.select], messages);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case 'GridClearSelect': {
|
|
308
|
+
const selectionInstance = this.getSelectionInstance();
|
|
309
|
+
if (!selectionInstance) {
|
|
310
|
+
this.updateLastMessage(messages, this.ctx.localization?.get('aiAssistantSelectionNotEnabled'));
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
this.applySelectionState(selectionInstance, []);
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case 'GridColumnResize': {
|
|
317
|
+
const col = findColumnById(cmd.id);
|
|
318
|
+
if (!col) {
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
// parse size (accept numeric or strings like '200px')
|
|
322
|
+
let newWidth;
|
|
323
|
+
if (typeof cmd.size === 'number') {
|
|
324
|
+
newWidth = cmd.size;
|
|
325
|
+
}
|
|
326
|
+
else if (typeof cmd.size === 'string') {
|
|
327
|
+
const numericPart = parseFloat(cmd.size);
|
|
328
|
+
if (!isNaN(numericPart)) {
|
|
329
|
+
newWidth = numericPart;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (typeof newWidth === 'number') {
|
|
333
|
+
const oldWidth = col.width;
|
|
334
|
+
// set the column width (ColumnBase.width setter handles string -> number)
|
|
335
|
+
col.width = newWidth;
|
|
336
|
+
// emit columnResize event with ColumnResizeArgs[]
|
|
337
|
+
const args = [{ column: col, oldWidth: oldWidth, newWidth: newWidth }];
|
|
338
|
+
this.ctx.grid.columnResize.emit(args);
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case 'GridColumnReorder': {
|
|
343
|
+
const col = findColumnById(cmd.id);
|
|
344
|
+
if (!col) {
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
const newPosition = Number(cmd.position);
|
|
348
|
+
if (!isNaN(newPosition) && newPosition >= 0) {
|
|
349
|
+
this.changeColumnPosition(col, newPosition);
|
|
350
|
+
}
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case 'GridColumnShow':
|
|
354
|
+
case 'GridColumnHide': {
|
|
355
|
+
const col = findColumnById(cmd.id);
|
|
356
|
+
if (!col) {
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
const targetHidden = cmd.type === 'GridColumnHide';
|
|
360
|
+
const changed = updateColumnHierarchy(col, (current) => {
|
|
361
|
+
if (current.hidden === targetHidden) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
current.hidden = targetHidden;
|
|
365
|
+
return true;
|
|
366
|
+
});
|
|
367
|
+
if (changed.length) {
|
|
368
|
+
this.columnInfoService.changeVisibility(changed);
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
case 'GridColumnLock':
|
|
373
|
+
case 'GridColumnUnlock': {
|
|
374
|
+
const col = findColumnById(cmd.id);
|
|
375
|
+
if (!col) {
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
const targetLocked = cmd.type === 'GridColumnLock';
|
|
379
|
+
const changed = updateColumnHierarchy(col, (current) => {
|
|
380
|
+
if (current.locked === targetLocked) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
current.locked = targetLocked;
|
|
384
|
+
return true;
|
|
385
|
+
});
|
|
386
|
+
if (changed.length) {
|
|
387
|
+
this.columnInfoService.changeLocked(changed);
|
|
388
|
+
}
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case 'GridPage': {
|
|
392
|
+
this.processPageCommand(cmd);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
case 'GridPageSize': {
|
|
396
|
+
this.processPageSizeCommand(cmd);
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case 'GridExportExcel': {
|
|
400
|
+
this.runExportWithFileName(this.ctx.excelComponent, cmd.fileName, () => this.ctx.grid.saveAsExcel());
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
case 'GridExportPDF': {
|
|
404
|
+
this.runExportWithFileName(this.ctx.pdfComponent, cmd.fileName, () => this.ctx.grid.emitPDFExportEvent());
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
default:
|
|
408
|
+
// Unknown command - ignore
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
runExportWithFileName(component, fileName, action) {
|
|
414
|
+
if (!component || !fileName) {
|
|
415
|
+
action();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const previousFileName = component.fileName;
|
|
419
|
+
component.fileName = fileName;
|
|
420
|
+
action();
|
|
421
|
+
const isExcel = component === this.ctx.excelComponent;
|
|
422
|
+
if (isExcel) {
|
|
423
|
+
this.zone.runOutsideAngular(() => {
|
|
424
|
+
this.ctx.excelComponent.fileCreated.subscribe(() => {
|
|
425
|
+
component.fileName = previousFileName;
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
component.fileName = previousFileName;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
processPageCommand(command) {
|
|
434
|
+
const pageSize = this.getCurrentPageSizeValue();
|
|
435
|
+
if (!isPresent(pageSize) || pageSize <= 0) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const total = this.getTotalItemsCount();
|
|
439
|
+
const requestedPage = Number(command.page);
|
|
440
|
+
let targetPage = Number.isFinite(requestedPage) ? Math.floor(requestedPage) : 1;
|
|
441
|
+
if (targetPage < 1) {
|
|
442
|
+
targetPage = 1;
|
|
443
|
+
}
|
|
444
|
+
if (isPresent(total) && pageSize > 0) {
|
|
445
|
+
const maxPage = Math.max(1, Math.ceil(total / pageSize));
|
|
446
|
+
targetPage = Math.min(targetPage, maxPage);
|
|
447
|
+
}
|
|
448
|
+
const skip = (targetPage - 1) * pageSize;
|
|
449
|
+
this.emitGridPageChange(skip, pageSize);
|
|
450
|
+
}
|
|
451
|
+
processPageSizeCommand(command) {
|
|
452
|
+
const rawPageSize = Number(command.pageSize);
|
|
453
|
+
if (!Number.isFinite(rawPageSize)) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const newPageSize = Math.max(1, Math.floor(rawPageSize));
|
|
457
|
+
const skip = Math.max(0, this.ctx.grid?.skip ?? 0);
|
|
458
|
+
this.ensurePageSizeOption(newPageSize);
|
|
459
|
+
this.emitGridPageChange(skip, newPageSize);
|
|
460
|
+
}
|
|
461
|
+
emitGridPageChange(skip, take) {
|
|
462
|
+
const grid = this.ctx.grid;
|
|
463
|
+
const normalizedSkip = Math.max(0, Math.floor(skip));
|
|
464
|
+
const normalizedTake = Math.max(1, Math.floor(take));
|
|
465
|
+
grid.skip = normalizedSkip;
|
|
466
|
+
grid.pageSize = normalizedTake;
|
|
467
|
+
grid.pageChange.emit({ skip: normalizedSkip, take: normalizedTake });
|
|
468
|
+
}
|
|
469
|
+
ensurePageSizeOption(pageSize) {
|
|
470
|
+
const grid = this.ctx.grid;
|
|
471
|
+
if (!grid) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const pageable = grid.pageable;
|
|
475
|
+
if (!pageable || typeof pageable === 'boolean') {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const pageSizes = pageable.pageSizes;
|
|
479
|
+
if (!Array.isArray(pageSizes) || pageSizes.length === 0) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (pageSizes.includes(pageSize)) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const uniqueSizes = [pageSize, ...pageSizes.filter(size => size !== pageSize)];
|
|
486
|
+
grid.pageable = {
|
|
487
|
+
...pageable,
|
|
488
|
+
pageSizes: uniqueSizes
|
|
489
|
+
};
|
|
490
|
+
const changeDetector = grid?.changeDetectorRef;
|
|
491
|
+
if (changeDetector && typeof changeDetector.markForCheck === 'function') {
|
|
492
|
+
changeDetector.markForCheck();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
getCurrentPageSizeValue() {
|
|
496
|
+
const grid = this.ctx.grid;
|
|
497
|
+
if (!grid) {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
const candidates = [grid.pageSize, grid.currentState?.take, this.ctx.dataBindingDirective?.['state']?.take];
|
|
501
|
+
for (const candidate of candidates) {
|
|
502
|
+
if (typeof candidate === 'number' && candidate > 0) {
|
|
503
|
+
return candidate;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const pageable = grid.pageable;
|
|
507
|
+
if (pageable && typeof pageable === 'object' && Array.isArray(pageable.pageSizes)) {
|
|
508
|
+
const numericSize = pageable.pageSizes.find(size => typeof size === 'number' && size > 0);
|
|
509
|
+
if (numericSize) {
|
|
510
|
+
return numericSize;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
const originalData = this.ctx.dataBindingDirective?.['originalData'];
|
|
514
|
+
if (Array.isArray(originalData) && originalData.length > 0) {
|
|
515
|
+
return originalData.length;
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
getTotalItemsCount() {
|
|
520
|
+
const grid = this.ctx.grid;
|
|
521
|
+
if (!grid) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
const gridData = grid.data;
|
|
525
|
+
if (gridData && typeof gridData.total === 'number') {
|
|
526
|
+
return gridData.total;
|
|
527
|
+
}
|
|
528
|
+
const view = grid.view;
|
|
529
|
+
if (view && typeof view.total === 'number') {
|
|
530
|
+
return view.total;
|
|
531
|
+
}
|
|
532
|
+
const originalData = this.ctx.dataBindingDirective?.['originalData'];
|
|
533
|
+
if (Array.isArray(originalData)) {
|
|
534
|
+
return originalData.length;
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
getSelectionInstance() {
|
|
539
|
+
const selectionDirective = this.ctx.grid?.selectionDirective;
|
|
540
|
+
if (selectionDirective && typeof selectionDirective === 'object') {
|
|
541
|
+
return selectionDirective;
|
|
542
|
+
}
|
|
543
|
+
const defaultSelection = this.ctx.grid?.defaultSelection;
|
|
544
|
+
if (defaultSelection && typeof defaultSelection === 'object') {
|
|
545
|
+
return defaultSelection;
|
|
546
|
+
}
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
updateLastMessage(messages, newMessage) {
|
|
550
|
+
if (!messages.length) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
messages[messages.length - 1] = newMessage;
|
|
554
|
+
}
|
|
555
|
+
getHighlightItems(descriptors) {
|
|
556
|
+
if (!descriptors?.length) {
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
const data = this.ctx.dataBindingDirective?.['originalData'] || [];
|
|
560
|
+
return highlightBy(data, descriptors, this.columns);
|
|
561
|
+
}
|
|
562
|
+
processSelectionResponse(selection, messages) {
|
|
563
|
+
const selectionInstance = this.getSelectionInstance();
|
|
564
|
+
if (!selectionInstance) {
|
|
565
|
+
this.updateLastMessage(messages, this.ctx.localization?.get('aiAssistantSelectionNotEnabled'));
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const descriptors = (selection || []).filter((descriptor) => Boolean(descriptor));
|
|
569
|
+
if (descriptors.length === 0) {
|
|
570
|
+
this.applySelectionState(selectionInstance, []);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const highlightItems = this.getHighlightItems(descriptors);
|
|
574
|
+
if (!highlightItems.length) {
|
|
575
|
+
this.applySelectionState(selectionInstance, []);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const hasCellSelections = highlightItems.some(item => isPresent(item.columnKey));
|
|
579
|
+
const hasRowSelections = highlightItems.some(item => !isPresent(item.columnKey));
|
|
580
|
+
const isCellMode = selectionInstance.isCellSelectionMode;
|
|
581
|
+
if ((!isCellMode && hasCellSelections) || (isCellMode && hasRowSelections)) {
|
|
582
|
+
const key = isCellMode ? 'aiAssistantSelectionRowModeRequired' : 'aiAssistantSelectionCellModeRequired';
|
|
583
|
+
this.updateLastMessage(messages, this.ctx.localization?.get(key));
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const selectionState = this.mapHighlightItemsToSelection(selectionInstance, highlightItems, isCellMode);
|
|
587
|
+
this.applySelectionState(selectionInstance, selectionState);
|
|
588
|
+
}
|
|
589
|
+
mapHighlightItemsToSelection(selectionInstance, highlightItems, isCellMode) {
|
|
590
|
+
const data = this.ctx.dataBindingDirective?.['originalData'] || [];
|
|
591
|
+
if (isCellMode) {
|
|
592
|
+
const mapped = highlightItems
|
|
593
|
+
.filter(item => isPresent(item.itemKey) && isPresent(item.columnKey))
|
|
594
|
+
.map(item => {
|
|
595
|
+
const rowIndex = item.itemKey;
|
|
596
|
+
const columnIndex = item.columnKey;
|
|
597
|
+
const dataItem = data[rowIndex];
|
|
598
|
+
if (!isPresent(dataItem)) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
if (typeof selectionInstance['getSelectionItem'] === 'function') {
|
|
602
|
+
const columnComponent = this.leafColumns[columnIndex];
|
|
603
|
+
const selectionItem = selectionInstance['getSelectionItem']({ dataItem, index: rowIndex }, columnComponent, columnIndex);
|
|
604
|
+
if (selectionItem && isPresent(selectionItem.itemKey) && isPresent(selectionItem.columnKey)) {
|
|
605
|
+
return selectionItem;
|
|
606
|
+
}
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
const itemKey = typeof selectionInstance.getItemKey === 'function'
|
|
610
|
+
? selectionInstance.getItemKey({ dataItem, index: rowIndex })
|
|
611
|
+
: rowIndex;
|
|
612
|
+
return isPresent(itemKey) ? { itemKey, columnKey: columnIndex } : null;
|
|
613
|
+
})
|
|
614
|
+
.filter((item) => isPresent(item));
|
|
615
|
+
return mapped.filter((item, index, self) => self.findIndex(other => other.itemKey === item.itemKey && other.columnKey === item.columnKey) === index);
|
|
616
|
+
}
|
|
617
|
+
const rowKeys = highlightItems
|
|
618
|
+
.filter(item => isPresent(item.itemKey))
|
|
619
|
+
.map(item => {
|
|
620
|
+
const rowIndex = item.itemKey;
|
|
621
|
+
const dataItem = data[rowIndex];
|
|
622
|
+
if (!isPresent(dataItem)) {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
if (typeof selectionInstance.getItemKey === 'function') {
|
|
626
|
+
return selectionInstance.getItemKey({ dataItem, index: rowIndex });
|
|
627
|
+
}
|
|
628
|
+
return rowIndex;
|
|
629
|
+
})
|
|
630
|
+
.filter(isPresent);
|
|
631
|
+
return Array.from(new Set(rowKeys));
|
|
632
|
+
}
|
|
633
|
+
applySelectionState(selectionInstance, selectionState) {
|
|
634
|
+
selectionInstance.selectedKeys = selectionState;
|
|
635
|
+
if (typeof selectionInstance['setState'] === 'function') {
|
|
636
|
+
selectionInstance['setState'](selectionState);
|
|
637
|
+
}
|
|
638
|
+
const changeDetector = selectionInstance['cd'];
|
|
639
|
+
if (changeDetector && typeof changeDetector.markForCheck === 'function') {
|
|
640
|
+
changeDetector.markForCheck();
|
|
641
|
+
}
|
|
642
|
+
if (typeof selectionInstance['notifyChange'] === 'function') {
|
|
643
|
+
selectionInstance['notifyChange']();
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
replaceQuotedColumnId(message, replacement) {
|
|
647
|
+
if (!replacement) {
|
|
648
|
+
const columnIdPattern = /(?:"|")(k-grid\d+-col\d+)(?:"|")\s*/g;
|
|
649
|
+
return message.replace(columnIdPattern, '').replace(/\s{2,}/g, ' ').trim();
|
|
650
|
+
}
|
|
651
|
+
const columnIdPattern = /(?:"|")(k-grid\d+-col\d+)(?:"|")/g;
|
|
652
|
+
return message.replace(columnIdPattern, (match) => {
|
|
653
|
+
const isEncoded = match.startsWith('"');
|
|
654
|
+
return isEncoded ? `"${replacement}"` : `"${replacement}"`;
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
isColumnCommand(type) {
|
|
658
|
+
return type === 'GridColumnResize' ||
|
|
659
|
+
type === 'GridColumnReorder' ||
|
|
660
|
+
type === 'GridColumnShow' ||
|
|
661
|
+
type === 'GridColumnHide' ||
|
|
662
|
+
type === 'GridColumnLock' ||
|
|
663
|
+
type === 'GridColumnUnlock';
|
|
664
|
+
}
|
|
665
|
+
getColumnReplacement(column) {
|
|
666
|
+
if (!column) {
|
|
667
|
+
return '';
|
|
668
|
+
}
|
|
669
|
+
if (column.title && String(column.title).trim()) {
|
|
670
|
+
return String(column.title).trim();
|
|
671
|
+
}
|
|
672
|
+
if (column.field && String(column.field).trim()) {
|
|
673
|
+
return String(column.field).trim();
|
|
674
|
+
}
|
|
675
|
+
return '';
|
|
676
|
+
}
|
|
677
|
+
processHighlightResponse(highlight) {
|
|
678
|
+
const highlightedItems = this.getHighlightItems(highlight);
|
|
192
679
|
this.ctx.highlightDirective['setState'](highlightedItems);
|
|
193
680
|
}
|
|
194
681
|
processFilterResponse(filter) {
|
|
@@ -212,7 +699,48 @@ export class AiAssistantComponent {
|
|
|
212
699
|
this.ctx.grid.filterChange.next(mergedFilter);
|
|
213
700
|
}
|
|
214
701
|
}
|
|
215
|
-
|
|
702
|
+
changeColumnPosition(column, newPosition) {
|
|
703
|
+
const grid = this.ctx.grid;
|
|
704
|
+
if (!grid?.columns) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const currentColumns = grid.columns.toArray();
|
|
708
|
+
const currentIndex = currentColumns.findIndex(col => col === column);
|
|
709
|
+
if (currentIndex === -1) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (newPosition < 0 || newPosition >= currentColumns.length) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const sortedColumns = currentColumns
|
|
716
|
+
.map((col, idx) => ({ col, physicalIndex: idx, visualOrder: col.orderIndex ?? idx }))
|
|
717
|
+
.sort((a, b) => a.visualOrder - b.visualOrder);
|
|
718
|
+
const currentVisualPos = sortedColumns.findIndex(item => item.physicalIndex === currentIndex);
|
|
719
|
+
if (currentVisualPos === newPosition) {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
currentColumns.forEach((col, idx) => {
|
|
723
|
+
const sortedIndex = sortedColumns.findIndex(item => item.physicalIndex === idx);
|
|
724
|
+
if (idx === currentIndex) {
|
|
725
|
+
col.orderIndex = newPosition;
|
|
726
|
+
}
|
|
727
|
+
else if (currentVisualPos < newPosition) {
|
|
728
|
+
col.orderIndex = (sortedIndex > currentVisualPos && sortedIndex <= newPosition)
|
|
729
|
+
? sortedIndex - 1 : sortedIndex;
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
col.orderIndex = (sortedIndex >= newPosition && sortedIndex < currentVisualPos)
|
|
733
|
+
? sortedIndex + 1 : sortedIndex;
|
|
734
|
+
}
|
|
735
|
+
col.isReordered = true;
|
|
736
|
+
});
|
|
737
|
+
grid.columnReorder.emit({
|
|
738
|
+
column: column,
|
|
739
|
+
oldIndex: currentVisualPos,
|
|
740
|
+
newIndex: newPosition
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AiAssistantComponent, deps: [{ token: i1.HttpClient }, { token: i2.ContextService }, { token: i3.ColumnInfoService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
|
|
216
744
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AiAssistantComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "aiPrompt", first: true, predicate: AIPromptComponent, descendants: true }], ngImport: i0, template: `
|
|
217
745
|
<kendo-aiprompt
|
|
218
746
|
#aiPrompt
|
|
@@ -308,7 +836,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
308
836
|
</kendo-aiprompt>
|
|
309
837
|
`
|
|
310
838
|
}]
|
|
311
|
-
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ContextService }, { type: i3.ColumnInfoService }], propDecorators: { aiPrompt: [{
|
|
839
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ContextService }, { type: i3.ColumnInfoService }, { type: i0.NgZone }], propDecorators: { aiPrompt: [{
|
|
312
840
|
type: ViewChild,
|
|
313
841
|
args: [AIPromptComponent]
|
|
314
842
|
}] } });
|
|
@@ -18,7 +18,7 @@ export const DEFAULT_AI_REQUEST_OPTIONS = {
|
|
|
18
18
|
/**
|
|
19
19
|
* Represents the event data when the AI Assistant request completes successfully.
|
|
20
20
|
*/
|
|
21
|
-
export class
|
|
21
|
+
export class GridAIAssistantResponseSuccessEvent extends PreventableEvent {
|
|
22
22
|
/**
|
|
23
23
|
* The HTTP response from the AI service.
|
|
24
24
|
*/
|
|
@@ -31,7 +31,7 @@ export class GridToolbarAIResponseSuccessEvent extends PreventableEvent {
|
|
|
31
31
|
/**
|
|
32
32
|
* Represents the event data when the AI Assistant request completes with an error.
|
|
33
33
|
*/
|
|
34
|
-
export class
|
|
34
|
+
export class GridAIAssistantResponseErrorEvent extends PreventableEvent {
|
|
35
35
|
/**
|
|
36
36
|
* The HTTP error response from the AI service.
|
|
37
37
|
*/
|