@progress/kendo-angular-grid 19.3.0-develop.3 → 19.3.0-develop.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/columns/columns-container.d.ts +2 -0
  2. package/common/data-layout-mode.d.ts +19 -0
  3. package/common/provider.service.d.ts +2 -0
  4. package/common/stacked-layout-settings.d.ts +24 -0
  5. package/data/data-mapping.service.d.ts +3 -1
  6. package/directives.d.ts +7 -4
  7. package/editing-directives/editing-directive-base.d.ts +3 -0
  8. package/esm2022/adaptiveness/adaptive-renderer.component.mjs +1 -2
  9. package/esm2022/column-menu/column-list.component.mjs +10 -10
  10. package/esm2022/column-menu/column-menu.component.mjs +1 -1
  11. package/esm2022/columns/column-base.mjs +4 -4
  12. package/esm2022/columns/column.component.mjs +1 -1
  13. package/esm2022/columns/columns-container.mjs +3 -0
  14. package/esm2022/common/column-info.service.mjs +1 -1
  15. package/esm2022/common/data-layout-mode.mjs +5 -0
  16. package/esm2022/common/provider.service.mjs +1 -0
  17. package/esm2022/common/stacked-layout-settings.mjs +5 -0
  18. package/esm2022/common/toolbar-tool-base.directive.mjs +3 -2
  19. package/esm2022/data/data-mapping.service.mjs +14 -3
  20. package/esm2022/directives.mjs +8 -1
  21. package/esm2022/editing/form/form-formfield.component.mjs +2 -2
  22. package/esm2022/editing-directives/editing-directive-base.mjs +17 -2
  23. package/esm2022/editing-directives/in-cell-editing.directive.mjs +3 -1
  24. package/esm2022/filtering/cell/boolean-filter-cell.component.mjs +1 -1
  25. package/esm2022/filtering/cell/date-filter-cell.component.mjs +1 -1
  26. package/esm2022/filtering/cell/filter-cell-operators.component.mjs +1 -2
  27. package/esm2022/filtering/cell/numeric-filter-cell.component.mjs +1 -1
  28. package/esm2022/filtering/cell/string-filter-cell.component.mjs +1 -1
  29. package/esm2022/filtering/filter-row.component.mjs +6 -3
  30. package/esm2022/filtering/menu/boolean-filter-menu.component.mjs +1 -2
  31. package/esm2022/filtering/menu/date-filter-menu-input.component.mjs +1 -1
  32. package/esm2022/filtering/menu/date-filter-menu.component.mjs +1 -1
  33. package/esm2022/filtering/menu/filter-menu-input-wrapper.component.mjs +1 -1
  34. package/esm2022/filtering/menu/filter-menu.component.mjs +1 -1
  35. package/esm2022/filtering/menu/numeric-filter-menu-input.component.mjs +1 -1
  36. package/esm2022/filtering/menu/numeric-filter-menu.component.mjs +1 -1
  37. package/esm2022/filtering/menu/string-filter-menu-input.component.mjs +1 -1
  38. package/esm2022/filtering/menu/string-filter-menu.component.mjs +1 -1
  39. package/esm2022/grid.component.mjs +190 -39
  40. package/esm2022/grid.module.mjs +103 -100
  41. package/esm2022/grouping/group-header.component.mjs +39 -4
  42. package/esm2022/grouping/group-panel.component.mjs +7 -3
  43. package/esm2022/highlight/highlight-item.mjs +5 -0
  44. package/esm2022/highlight/highlight.directive.mjs +132 -0
  45. package/esm2022/index.mjs +4 -0
  46. package/esm2022/localization/messages.mjs +57 -3
  47. package/esm2022/navigation/default-focusable-element.mjs +14 -2
  48. package/esm2022/navigation/focusable.directive.mjs +1 -1
  49. package/esm2022/navigation/navigation-cursor.mjs +7 -1
  50. package/esm2022/navigation/navigation-metadata.mjs +3 -1
  51. package/esm2022/navigation/navigation.service.mjs +136 -5
  52. package/esm2022/navigation/toolbar-tool-name.mjs +2 -1
  53. package/esm2022/package-metadata.mjs +2 -2
  54. package/esm2022/pdf/export-element.mjs +14 -5
  55. package/esm2022/pdf/pdf.component.mjs +3 -1
  56. package/esm2022/rendering/cell.component.mjs +466 -188
  57. package/esm2022/rendering/common/col-group.component.mjs +20 -7
  58. package/esm2022/rendering/footer/footer.component.mjs +117 -54
  59. package/esm2022/rendering/header/header.component.mjs +5 -2
  60. package/esm2022/rendering/list.component.mjs +14 -9
  61. package/esm2022/rendering/table-body.component.mjs +388 -171
  62. package/esm2022/rendering/toolbar/tools/ai-assistant/ai-assistant.component.mjs +290 -0
  63. package/esm2022/rendering/toolbar/tools/ai-assistant/ai-tool.directive.mjs +269 -0
  64. package/esm2022/rendering/toolbar/tools/ai-assistant/utils.mjs +47 -0
  65. package/esm2022/rendering/toolbar/tools/select-all-command-tool.directive.mjs +93 -0
  66. package/esm2022/row-reordering/row-reorder.service.mjs +2 -2
  67. package/esm2022/row-reordering/utils.mjs +6 -4
  68. package/esm2022/selection/cell-selection.service.mjs +6 -3
  69. package/esm2022/selection/pair-set.mjs +87 -10
  70. package/esm2022/utils.mjs +0 -4
  71. package/fesm2022/progress-kendo-angular-grid.mjs +2460 -599
  72. package/filtering/filter-row.component.d.ts +1 -0
  73. package/grid.component.d.ts +29 -1
  74. package/grid.module.d.ts +102 -99
  75. package/grouping/group-header.component.d.ts +1 -0
  76. package/highlight/highlight-item.d.ts +17 -0
  77. package/highlight/highlight.directive.d.ts +56 -0
  78. package/index.d.ts +7 -0
  79. package/localization/messages.d.ts +39 -3
  80. package/navigation/default-focusable-element.d.ts +3 -1
  81. package/navigation/focus-group.d.ts +1 -1
  82. package/navigation/navigation-metadata.d.ts +2 -1
  83. package/navigation/navigation.service.d.ts +6 -0
  84. package/navigation/toolbar-tool-name.d.ts +1 -0
  85. package/package.json +22 -21
  86. package/rendering/cell.component.d.ts +30 -15
  87. package/rendering/common/col-group.component.d.ts +5 -0
  88. package/rendering/footer/footer.component.d.ts +4 -1
  89. package/rendering/header/header.component.d.ts +1 -0
  90. package/rendering/list.component.d.ts +4 -1
  91. package/rendering/table-body.component.d.ts +3 -1
  92. package/rendering/toolbar/tools/ai-assistant/ai-assistant.component.d.ts +50 -0
  93. package/rendering/toolbar/tools/ai-assistant/ai-tool.directive.d.ts +119 -0
  94. package/rendering/toolbar/tools/ai-assistant/utils.d.ts +110 -0
  95. package/rendering/toolbar/tools/select-all-command-tool.directive.d.ts +36 -0
  96. package/row-reordering/row-reorder.service.d.ts +1 -1
  97. package/row-reordering/utils.d.ts +1 -1
  98. package/schematics/ngAdd/index.js +4 -4
  99. package/selection/cell-selection.service.d.ts +1 -0
  100. package/selection/pair-set.d.ts +36 -8
  101. package/utils.d.ts +0 -4
@@ -0,0 +1,290 @@
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 { Component, ViewChild } from '@angular/core';
6
+ import { AIPromptComponent, OutputViewComponent, PromptViewComponent, AIPromptCustomMessagesComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective } from '@progress/kendo-angular-conversational-ui';
7
+ import { HttpClient, HttpRequest } from '@angular/common/http';
8
+ import { ContextService } from './../../../../common/provider.service';
9
+ import { ColumnInfoService } from './../../../../common/column-info.service';
10
+ import { convertDateStringsInFilter } from './utils';
11
+ import { NgIf } from '@angular/common';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "@angular/common/http";
14
+ import * as i2 from "./../../../../common/provider.service";
15
+ import * as i3 from "./../../../../common/column-info.service";
16
+ /**
17
+ * @hidden
18
+ */
19
+ export class AiAssistantComponent {
20
+ http;
21
+ ctx;
22
+ columnInfoService;
23
+ aiPrompt;
24
+ activeView = 0;
25
+ promptOutputs = [];
26
+ requestUrl;
27
+ requestOptions;
28
+ aiPromptSettings;
29
+ aiToolDirective;
30
+ streaming = false;
31
+ disabledGenerateButton = false;
32
+ lastMessage;
33
+ requestData;
34
+ currentRequestSubscription = null;
35
+ //Remove this when the AI Assistant has a built-in loading indicator
36
+ loadingOutput = { id: 'k-loading-item', output: '', prompt: '' };
37
+ columns = [];
38
+ idCounter = 0;
39
+ constructor(http, ctx, columnInfoService) {
40
+ this.http = http;
41
+ this.ctx = ctx;
42
+ this.columnInfoService = columnInfoService;
43
+ }
44
+ ngAfterViewInit() {
45
+ this.columns = this.columnInfoService.leafNamedColumns.map((col) => ({
46
+ field: col.field,
47
+ title: col.title || col.field
48
+ }));
49
+ }
50
+ ngOnDestroy() {
51
+ this.unsubscribeCurrentRequest();
52
+ }
53
+ message(message) {
54
+ return this.ctx.localization.get(message);
55
+ }
56
+ cancelRequest() {
57
+ this.aiToolDirective.cancelRequest.emit();
58
+ this.unsubscribeCurrentRequest();
59
+ this.streaming = false;
60
+ }
61
+ onPromptRequest(ev) {
62
+ if (this.promptOutputs.length === 0) {
63
+ this.promptOutputs.push(this.loadingOutput);
64
+ }
65
+ this.unsubscribeCurrentRequest();
66
+ this.streaming = true;
67
+ this.activeView = 1;
68
+ if (ev.prompt) {
69
+ this.lastMessage = ev.prompt;
70
+ }
71
+ this.requestData = {
72
+ columns: this.columns,
73
+ promptMessage: ev.prompt,
74
+ url: this.requestUrl,
75
+ requestOptions: {
76
+ ...this.requestOptions
77
+ }
78
+ };
79
+ if (!this.requestOptions.body) {
80
+ const requestBody = {
81
+ role: this.requestData.requestOptions.role,
82
+ contents: [
83
+ {
84
+ text: this.requestData.promptMessage
85
+ }
86
+ ],
87
+ columns: this.requestData.columns
88
+ };
89
+ this.requestData.requestOptions.body = requestBody;
90
+ }
91
+ this.aiToolDirective.promptRequest.emit({ requestData: this.requestData, isRetry: ev.isRetry });
92
+ if (!this.requestUrl) {
93
+ return;
94
+ }
95
+ this.currentRequestSubscription = this.sendPromptRequest().subscribe((res) => {
96
+ if (res.body) {
97
+ this.processResponse(res);
98
+ this.streaming = false;
99
+ }
100
+ this.currentRequestSubscription = null;
101
+ }, (error) => {
102
+ this.handleError(error);
103
+ this.streaming = false;
104
+ this.currentRequestSubscription = null;
105
+ });
106
+ }
107
+ sendPromptRequest() {
108
+ const request = new HttpRequest(this.requestData.requestOptions.method, this.requestData.url, this.requestData.requestOptions.body, this.requestData.requestOptions);
109
+ return this.http.request(request);
110
+ }
111
+ processResponse(response) {
112
+ const responseBody = response.body;
113
+ const isFilterable = Boolean(this.ctx.grid.filterable);
114
+ const isSortable = Boolean(this.ctx.grid.sortable);
115
+ const isGroupable = Boolean(this.ctx.grid.groupable);
116
+ if (isFilterable && responseBody.filter) {
117
+ this.processFilterResponse(responseBody.filter);
118
+ }
119
+ if (isSortable && responseBody.sort) {
120
+ this.processArrayResponse(responseBody.sort, this.ctx.grid.currentState.sort || [], (item) => item.field, (mergedArray) => this.ctx.grid.sortChange.next(mergedArray));
121
+ }
122
+ if (isGroupable && responseBody.group) {
123
+ this.processArrayResponse(responseBody.group, this.ctx.grid.currentState.group || [], (item) => item.field, (mergedArray) => this.ctx.grid.groupChange.next(mergedArray));
124
+ }
125
+ const responseContentStart = [`${this.ctx.localization.get('aiAssistantOutputCardBodyContent')} \n`];
126
+ const responseContentBody = responseBody.messages
127
+ .map((output, idx) => `${idx + 1} ${output}`)
128
+ .join('\n');
129
+ const output = {
130
+ id: this.idCounter++,
131
+ title: this.ctx.localization.get('aiAssistantOutputCardTitle'),
132
+ prompt: this.lastMessage,
133
+ output: responseContentStart.concat(responseContentBody).join(''),
134
+ };
135
+ this.deleteDummyLoadingOutput();
136
+ this.promptOutputs.unshift(output);
137
+ this.aiToolDirective.responseSuccess.emit(response);
138
+ }
139
+ handleError(error) {
140
+ const output = {
141
+ id: this.idCounter++,
142
+ prompt: this.lastMessage,
143
+ output: error.message
144
+ };
145
+ this.deleteDummyLoadingOutput();
146
+ this.promptOutputs.unshift(output);
147
+ this.aiToolDirective.responseError.emit(error);
148
+ }
149
+ deleteDummyLoadingOutput() {
150
+ if (this.promptOutputs[0].id === this.loadingOutput.id) {
151
+ this.promptOutputs.splice(0, 1);
152
+ }
153
+ }
154
+ unsubscribeCurrentRequest() {
155
+ if (this.currentRequestSubscription) {
156
+ this.currentRequestSubscription.unsubscribe();
157
+ this.currentRequestSubscription = null;
158
+ }
159
+ }
160
+ processArrayResponse(newItems, currentItems, getField, updateGrid) {
161
+ if (newItems?.length === 0) {
162
+ updateGrid([]);
163
+ }
164
+ else if (newItems?.length) {
165
+ let mergedArray = [...newItems];
166
+ const newFields = newItems.map(getField);
167
+ const existingItemsToKeep = currentItems.filter(item => !newFields.includes(getField(item)));
168
+ mergedArray = [...mergedArray, ...existingItemsToKeep];
169
+ updateGrid(mergedArray);
170
+ }
171
+ }
172
+ processFilterResponse(filter) {
173
+ const processedFilter = convertDateStringsInFilter(filter, this.columnInfoService.leafNamedColumns);
174
+ const clearFilter = Object.keys(processedFilter).length === 0;
175
+ if (clearFilter) {
176
+ this.ctx.grid.filterChange.next(undefined);
177
+ }
178
+ else if (processedFilter?.filters.length) {
179
+ const currentFilter = this.ctx.grid.currentState.filter;
180
+ let mergedFilter = processedFilter;
181
+ if (currentFilter && currentFilter.filters?.length > 0) {
182
+ mergedFilter = {
183
+ logic: 'and',
184
+ filters: [
185
+ currentFilter,
186
+ processedFilter
187
+ ]
188
+ };
189
+ }
190
+ this.ctx.grid.filterChange.next(mergedFilter);
191
+ }
192
+ }
193
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AiAssistantComponent, deps: [{ token: i1.HttpClient }, { token: i2.ContextService }, { token: i3.ColumnInfoService }], target: i0.ɵɵFactoryTarget.Component });
194
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: AiAssistantComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "aiPrompt", first: true, predicate: AIPromptComponent, descendants: true }], ngImport: i0, template: `
195
+ <kendo-aiprompt
196
+ #aiPrompt
197
+ [promptSuggestions]="aiPromptSettings?.promptSuggestions"
198
+ [showOutputRating]="aiPromptSettings?.showOutputRating"
199
+ [streaming]="streaming"
200
+ [speechToTextButton]="aiPromptSettings?.speechToTextButton"
201
+ [(activeView)]="activeView"
202
+ [generateButtonSVGIcon]="aiPromptSettings?.generateButtonSVGIcon"
203
+ [generateButtonIcon]="aiPromptSettings?.generateButtonIcon"
204
+ [disabledGenerateButton]="disabledGenerateButton || promptView.textAreaValue?.length === 0"
205
+ [promptOutputs]="aiPromptSettings?.promptOutputs || promptOutputs"
206
+ (promptRequest)="onPromptRequest($event)"
207
+ (promptRequestCancel)="cancelRequest()"
208
+ >
209
+ <kendo-aiprompt-prompt-view #promptView></kendo-aiprompt-prompt-view>
210
+ <kendo-aiprompt-output-view></kendo-aiprompt-output-view>
211
+ <ng-template *ngIf="streaming && aiPrompt.streaming" kendoAIPromptOutputTemplate let-output>
212
+ <div class="k-card">
213
+ <div class="k-card-header">
214
+ <div class="k-card-title">
215
+ <span class="k-skeleton k-skeleton-text k-skeleton-pulse" [style.width.px]="200"></span>
216
+ </div>
217
+ <div class="k-card-subtitle">
218
+ <span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
219
+ </div>
220
+ </div>
221
+ <div class="k-card-body">
222
+ <span class="k-skeleton k-skeleton-rect k-skeleton-pulse" style="height: 80px;"></span>
223
+ </div>
224
+ <div class="k-card-actions">
225
+ <span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
226
+ </div>
227
+ </div>
228
+ </ng-template>
229
+ <ng-template *ngIf="!(streaming && aiPrompt.streaming)" kendoAIPromptOutputBodyTemplate let-output>
230
+ <p>{{output.output}}</p>
231
+ </ng-template>
232
+ <kendo-aiprompt-messages
233
+ [generateOutput]="message('aiAssistantApplyButtonText')"
234
+ ></kendo-aiprompt-messages>
235
+ </kendo-aiprompt>
236
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AIPromptComponent, selector: "kendo-aiprompt", inputs: ["activeView", "promptCommands", "promptSuggestions", "promptOutputs", "showOutputRating", "streaming", "speechToTextButton", "textAreaSettings", "generateButtonSVGIcon", "generateButtonIcon", "disabledGenerateButton"], outputs: ["activeViewChange", "promptRequest", "commandExecute", "outputCopy", "outputRatingChange", "promptRequestCancel"], exportAs: ["kendoAIPrompt"] }, { kind: "component", type: AIPromptCustomMessagesComponent, selector: "kendo-aiprompt-messages" }, { kind: "component", type: PromptViewComponent, selector: "kendo-aiprompt-prompt-view" }, { kind: "component", type: OutputViewComponent, selector: "kendo-aiprompt-output-view" }, { kind: "directive", type: AIPromptOutputTemplateDirective, selector: "[kendoAIPromptOutputTemplate]" }, { kind: "directive", type: AIPromptOutputBodyTemplateDirective, selector: "[kendoAIPromptOutputBodyTemplate]" }] });
237
+ }
238
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AiAssistantComponent, decorators: [{
239
+ type: Component,
240
+ args: [{
241
+ standalone: true,
242
+ imports: [NgIf, AIPromptComponent, AIPromptCustomMessagesComponent, PromptViewComponent, OutputViewComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective],
243
+ template: `
244
+ <kendo-aiprompt
245
+ #aiPrompt
246
+ [promptSuggestions]="aiPromptSettings?.promptSuggestions"
247
+ [showOutputRating]="aiPromptSettings?.showOutputRating"
248
+ [streaming]="streaming"
249
+ [speechToTextButton]="aiPromptSettings?.speechToTextButton"
250
+ [(activeView)]="activeView"
251
+ [generateButtonSVGIcon]="aiPromptSettings?.generateButtonSVGIcon"
252
+ [generateButtonIcon]="aiPromptSettings?.generateButtonIcon"
253
+ [disabledGenerateButton]="disabledGenerateButton || promptView.textAreaValue?.length === 0"
254
+ [promptOutputs]="aiPromptSettings?.promptOutputs || promptOutputs"
255
+ (promptRequest)="onPromptRequest($event)"
256
+ (promptRequestCancel)="cancelRequest()"
257
+ >
258
+ <kendo-aiprompt-prompt-view #promptView></kendo-aiprompt-prompt-view>
259
+ <kendo-aiprompt-output-view></kendo-aiprompt-output-view>
260
+ <ng-template *ngIf="streaming && aiPrompt.streaming" kendoAIPromptOutputTemplate let-output>
261
+ <div class="k-card">
262
+ <div class="k-card-header">
263
+ <div class="k-card-title">
264
+ <span class="k-skeleton k-skeleton-text k-skeleton-pulse" [style.width.px]="200"></span>
265
+ </div>
266
+ <div class="k-card-subtitle">
267
+ <span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
268
+ </div>
269
+ </div>
270
+ <div class="k-card-body">
271
+ <span class="k-skeleton k-skeleton-rect k-skeleton-pulse" style="height: 80px;"></span>
272
+ </div>
273
+ <div class="k-card-actions">
274
+ <span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
275
+ </div>
276
+ </div>
277
+ </ng-template>
278
+ <ng-template *ngIf="!(streaming && aiPrompt.streaming)" kendoAIPromptOutputBodyTemplate let-output>
279
+ <p>{{output.output}}</p>
280
+ </ng-template>
281
+ <kendo-aiprompt-messages
282
+ [generateOutput]="message('aiAssistantApplyButtonText')"
283
+ ></kendo-aiprompt-messages>
284
+ </kendo-aiprompt>
285
+ `
286
+ }]
287
+ }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: i2.ContextService }, { type: i3.ColumnInfoService }]; }, propDecorators: { aiPrompt: [{
288
+ type: ViewChild,
289
+ args: [AIPromptComponent]
290
+ }] } });
@@ -0,0 +1,269 @@
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 { Directive, NgZone, ChangeDetectorRef, Input, Output, EventEmitter } from '@angular/core';
6
+ import { RefreshService, ToolBarButtonComponent } from '@progress/kendo-angular-toolbar';
7
+ import { isPresent } from '@progress/kendo-angular-common';
8
+ import { Subscription } from 'rxjs';
9
+ import { sparklesIcon, tableWizardIcon } from '@progress/kendo-svg-icons';
10
+ import { ContextService } from '../../../../common/provider.service';
11
+ import { filter, take } from 'rxjs/operators';
12
+ import { ToolbarToolBase } from '../../../../common/toolbar-tool-base.directive';
13
+ import { ToolbarToolName } from '../../../../navigation/toolbar-tool-name';
14
+ import { WindowService } from '@progress/kendo-angular-dialog';
15
+ import { AiAssistantComponent } from './ai-assistant.component';
16
+ import { DEFAULT_AI_REQUEST_OPTIONS } from './utils';
17
+ import * as i0 from "@angular/core";
18
+ import * as i1 from "@progress/kendo-angular-dialog";
19
+ import * as i2 from "@progress/kendo-angular-toolbar";
20
+ import * as i3 from "../../../../common/provider.service";
21
+ /**
22
+ * Represents an AI Assistant tool of the Grid.
23
+ * Use this directive on any `kendo-toolbar-button` inside a ToolbarComponent in the Grid.
24
+ *
25
+ * @example
26
+ * ```html-no-run
27
+ * <kendo-grid>
28
+ * <kendo-toolbar>
29
+ * <kendo-toolbar-button kendoGridAIAssistantTool></kendo-toolbar-button>
30
+ * </kendo-toolbar>
31
+ * </kendo-grid>
32
+ * ```
33
+ * @remarks
34
+ * Applied to: {@link ToolBarButtonComponent}.
35
+ */
36
+ export class AIAssistantToolbarDirective extends ToolbarToolBase {
37
+ windowService;
38
+ host;
39
+ ctx;
40
+ zone;
41
+ refresh;
42
+ /**
43
+ * The URL to which the AI Assistant tool sends the AI request.
44
+ * - When you set this property, the AI Assistant tool sends and handles an HTTP request to the provided `requestUrl`. You can handle the `promptRequest` event to modify the request options before the tool sends it.
45
+ * - When you do not set this property, the AI Assistant tool does not send an HTTP request. You should handle the `promptRequest` event to send and handle a custom HTTP request.
46
+ */
47
+ requestUrl;
48
+ /**
49
+ * Configures the request options that the AI Assistant tool sends with the AI request.
50
+ *
51
+ * @default
52
+ * ```ts
53
+ * {
54
+ * headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
55
+ * role: 'user',
56
+ * method: 'POST',
57
+ * responseType: 'json',
58
+ * withCredentials: false
59
+ * }
60
+ * ```
61
+ */
62
+ requestOptions;
63
+ /**
64
+ * Configures the settings for the AI Assistant Window.
65
+ */
66
+ aiWindowSettings;
67
+ /**
68
+ * Configures the settings for the AI prompt component that the AI Assistant Window component uses.
69
+ */
70
+ aiPromptSettings;
71
+ /**
72
+ * Emits an event before the AI Assistant tool sends the AI request.
73
+ * - When you provide a `requestUrl`, you can handle the event to modify the request options.
74
+ * - When you do not provide a `requestUrl`, you can handle the event to perform an entirely custom request.
75
+ */
76
+ promptRequest = new EventEmitter();
77
+ /**
78
+ * Emits an event when the user clicks the cancel button.
79
+ */
80
+ cancelRequest = new EventEmitter();
81
+ /**
82
+ * Emits an event when the AI Assistant tool completes the AI request successfully.
83
+ * The event contains the response from the AI service.
84
+ */
85
+ responseSuccess = new EventEmitter();
86
+ /**
87
+ * Emits an event when the AI Assistant tool completes the AI request with an error.
88
+ * The event contains the response from the AI service.
89
+ */
90
+ responseError = new EventEmitter();
91
+ /**
92
+ * Emits an event when the AI Assistant tool closes.
93
+ */
94
+ close = new EventEmitter();
95
+ /**
96
+ * Emits an event when the AI Assistant tool opens.
97
+ * The event contains the window instance and the AI prompt instance.
98
+ */
99
+ open = new EventEmitter();
100
+ tableWizardIcon = tableWizardIcon;
101
+ windowRef;
102
+ subs = new Subscription();
103
+ actionSheetCloseSub;
104
+ emitOpenClose = false;
105
+ defaultAiPromptSettings = {
106
+ speechToTextButton: true,
107
+ generateButtonSVGIcon: this.tableWizardIcon,
108
+ generateButtonIcon: 'table-wizard'
109
+ };
110
+ constructor(windowService, host, ctx, zone, refresh, cdr) {
111
+ super(host, ToolbarToolName.aiAssistant, ctx, zone, cdr);
112
+ this.windowService = windowService;
113
+ this.host = host;
114
+ this.ctx = ctx;
115
+ this.zone = zone;
116
+ this.refresh = refresh;
117
+ this.host.rounded = 'full';
118
+ this.host.themeColor = 'primary';
119
+ this.host.showText = 'never';
120
+ }
121
+ ngOnInit() {
122
+ this.subs.add(this.host.click.subscribe(() => this.onClick()));
123
+ const hasToolbarIcon = isPresent(this.host.toolbarOptions.icon) && this.host.toolbarOptions.icon !== '';
124
+ const hasOverflowIcon = isPresent(this.host.overflowOptions.icon) && this.host.overflowOptions.icon !== '';
125
+ const hasIcon = hasToolbarIcon && hasOverflowIcon;
126
+ const hasSvgIcon = isPresent(this.host.toolbarOptions.svgIcon) && isPresent(this.host.overflowOptions.svgIcon);
127
+ if (!hasIcon) {
128
+ this.host.icon = 'sparkles';
129
+ }
130
+ if (!hasSvgIcon) {
131
+ this.host.svgIcon = sparklesIcon;
132
+ }
133
+ this.requestOptions = { ...DEFAULT_AI_REQUEST_OPTIONS, ...this.requestOptions };
134
+ }
135
+ ngAfterViewInit() {
136
+ super.ngAfterViewInit();
137
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
138
+ this.buttonElement?.setAttribute('aria-haspopup', 'dialog');
139
+ this.buttonElement?.setAttribute('aria-expanded', 'false');
140
+ const needsTitle = this.host.showText !== 'always' && this.host.showText !== 'toolbar';
141
+ if (needsTitle && !this.host.title) {
142
+ this.buttonElement?.setAttribute('title', this.ctx.localization.get('aiAssistantToolbarToolText'));
143
+ }
144
+ });
145
+ this.subs.add(this.refresh.onRefresh.pipe(filter((tool) => tool === this.host)).subscribe((tool) => {
146
+ if (tool.overflows && this.windowRef) {
147
+ this.windowRef.close();
148
+ }
149
+ }));
150
+ }
151
+ ngOnDestroy() {
152
+ super.ngOnDestroy();
153
+ this.subs.unsubscribe();
154
+ if (this.actionSheetCloseSub) {
155
+ this.actionSheetCloseSub.unsubscribe();
156
+ this.actionSheetCloseSub = null;
157
+ }
158
+ }
159
+ /**
160
+ * @hidden
161
+ */
162
+ onClick() {
163
+ this.emitOpenClose = true;
164
+ this.toggleWindow();
165
+ }
166
+ /**
167
+ * Toggles the AI Assistant window.
168
+ */
169
+ toggleWindow() {
170
+ if (!this.windowRef) {
171
+ this.openWindow();
172
+ }
173
+ else {
174
+ this.closeWindow();
175
+ }
176
+ }
177
+ openWindow() {
178
+ const defaultWindowWidth = 437;
179
+ const rtl = this.ctx.localization.rtl;
180
+ const defaultWindowSettings = {
181
+ left: rtl ? this.buttonElement.offsetLeft - (this.aiWindowSettings.width || defaultWindowWidth) : this.buttonElement.offsetLeft + this.buttonElement.offsetWidth,
182
+ top: this.buttonElement.offsetTop + this.buttonElement.offsetHeight,
183
+ width: defaultWindowWidth,
184
+ title: this.ctx.localization.get('aiAssistantWindowTitle'),
185
+ cssClass: 'k-grid-assistant-window',
186
+ content: AiAssistantComponent,
187
+ autoFocusedElement: '.k-input-inner',
188
+ appendTo: this.ctx.grid.windowContainer
189
+ };
190
+ this.aiWindowSettings = {
191
+ ...defaultWindowSettings,
192
+ ...this.aiWindowSettings
193
+ };
194
+ this.windowRef = this.windowService.open(this.aiWindowSettings);
195
+ this.windowRef.window.instance.messages = {
196
+ closeTitle: this.ctx.localization.get('aiAssistantWindowCloseTitle'),
197
+ maximizeTitle: this.ctx.localization.get('aiAssistantWindowMaximizeTitle'),
198
+ minimizeTitle: this.ctx.localization.get('aiAssistantWindowMinimizeTitle'),
199
+ restoreTitle: this.ctx.localization.get('aiAssistantWindowRestoreTitle')
200
+ };
201
+ const aiPrompt = this.windowRef.content.instance;
202
+ aiPrompt.requestUrl = this.requestUrl;
203
+ aiPrompt.requestOptions = this.requestOptions;
204
+ aiPrompt.aiToolDirective = this;
205
+ aiPrompt.disabledGenerateButton = this.aiPromptSettings?.disabledGenerateButton;
206
+ aiPrompt.streaming = this.aiPromptSettings?.streaming || false;
207
+ aiPrompt.activeView = this.aiPromptSettings?.activeView || 0;
208
+ aiPrompt.aiPromptSettings = { ...this.defaultAiPromptSettings, ...this.aiPromptSettings };
209
+ if (this.emitOpenClose) {
210
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
211
+ const event = {
212
+ aiWindow: this.windowRef.window.instance,
213
+ aiPrompt: this.windowRef.content.instance.aiPrompt
214
+ };
215
+ this.open.emit(event);
216
+ this.emitOpenClose = false;
217
+ });
218
+ }
219
+ this.subs.add(this.windowRef.window.instance.close.subscribe(() => {
220
+ this.emitOpenClose = true;
221
+ this.closeWindow(true);
222
+ }));
223
+ this.host.selected = true;
224
+ }
225
+ closeWindow(focusAnchor = false) {
226
+ this.windowRef.close();
227
+ if (this.emitOpenClose) {
228
+ this.close.emit();
229
+ this.emitOpenClose = false;
230
+ }
231
+ this.windowRef = null;
232
+ this.buttonElement?.setAttribute('aria-expanded', 'false');
233
+ this.buttonElement?.removeAttribute('aria-controls');
234
+ this.host.selected = false;
235
+ focusAnchor && this.buttonElement?.focus();
236
+ }
237
+ get buttonElement() {
238
+ return this.host.getButton();
239
+ }
240
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AIAssistantToolbarDirective, deps: [{ token: i1.WindowService }, { token: i2.ToolBarButtonComponent }, { token: i3.ContextService }, { token: i0.NgZone }, { token: i2.RefreshService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
241
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: AIAssistantToolbarDirective, isStandalone: true, selector: "[kendoGridAIAssistantTool]", inputs: { requestUrl: "requestUrl", requestOptions: "requestOptions", aiWindowSettings: "aiWindowSettings", aiPromptSettings: "aiPromptSettings" }, outputs: { promptRequest: "promptRequest", cancelRequest: "cancelRequest", responseSuccess: "responseSuccess", responseError: "responseError", close: "close", open: "open" }, usesInheritance: true, ngImport: i0 });
242
+ }
243
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AIAssistantToolbarDirective, decorators: [{
244
+ type: Directive,
245
+ args: [{
246
+ selector: '[kendoGridAIAssistantTool]',
247
+ standalone: true
248
+ }]
249
+ }], ctorParameters: function () { return [{ type: i1.WindowService }, { type: i2.ToolBarButtonComponent }, { type: i3.ContextService }, { type: i0.NgZone }, { type: i2.RefreshService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { requestUrl: [{
250
+ type: Input
251
+ }], requestOptions: [{
252
+ type: Input
253
+ }], aiWindowSettings: [{
254
+ type: Input
255
+ }], aiPromptSettings: [{
256
+ type: Input
257
+ }], promptRequest: [{
258
+ type: Output
259
+ }], cancelRequest: [{
260
+ type: Output
261
+ }], responseSuccess: [{
262
+ type: Output
263
+ }], responseError: [{
264
+ type: Output
265
+ }], close: [{
266
+ type: Output
267
+ }], open: [{
268
+ type: Output
269
+ }] } });
@@ -0,0 +1,47 @@
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 { HttpHeaders } from "@angular/common/http";
6
+ /**
7
+ * @hidden
8
+ */
9
+ export const DEFAULT_AI_REQUEST_OPTIONS = {
10
+ headers: new HttpHeaders({
11
+ 'Content-Type': 'application/json'
12
+ }),
13
+ role: 'user',
14
+ method: 'POST',
15
+ responseType: 'json'
16
+ };
17
+ /**
18
+ * @hidden
19
+ */
20
+ export const convertDateStringsInFilter = (filter, columns) => {
21
+ if (!filter) {
22
+ return filter;
23
+ }
24
+ if (filter.filters && Array.isArray(filter.filters)) {
25
+ return {
26
+ ...filter,
27
+ filters: filter.filters.map(f => convertDateStringsInFilter(f, columns))
28
+ };
29
+ }
30
+ if (filter.field && filter.value !== undefined) {
31
+ const column = columns.find(col => col.field === filter.field);
32
+ if (column && isDateField(filter.field, columns)) {
33
+ return {
34
+ ...filter,
35
+ value: new Date(filter.value)
36
+ };
37
+ }
38
+ }
39
+ return filter;
40
+ };
41
+ /**
42
+ * @hidden
43
+ */
44
+ export const isDateField = (fieldName, columns) => {
45
+ const column = columns.find((col) => col.field === fieldName);
46
+ return column?.filter === 'date' || column?.editor === 'date';
47
+ };