diff --git a/apps/react-storybook/stories/examples/datagrid/DataGrid.stories.tsx b/apps/react-storybook/stories/examples/datagrid/DataGrid.stories.tsx
index c15ff00ff869..76256142cca4 100644
--- a/apps/react-storybook/stories/examples/datagrid/DataGrid.stories.tsx
+++ b/apps/react-storybook/stories/examples/datagrid/DataGrid.stories.tsx
@@ -5,6 +5,8 @@ import { countries, generateData } from './data';
import DataGrid, {
Column,
DataGridTypes,
+ Editing,
+ Form,
Grouping,
GroupPanel,
Pager,
@@ -15,6 +17,7 @@ import DiscountCell from "./DiscountCell";
import ODataStore from "devextreme/data/odata/store";
import { AIIntegration } from 'devextreme-react/common/ai-integration';
import { AzureOpenAI } from 'openai';
+import notify from 'devextreme/ui/notify';
const columnOptions = {
regularColumns: [
@@ -241,8 +244,7 @@ export const ColumnReordering: Story = {
rtlEnabled: false,
columnHidingEnabled: true,
dataSource: countries,
- // @ts-expect-error
- columns: 'regularColumns',
+ columns: 'regularColumns' as any,
columnFixing: {
enabled: false
},
@@ -353,3 +355,167 @@ export const AiColumn: Story = {
allowColumnReordering: true,
},
};
+
+const employees = [
+ {
+ ID: 1,
+ FirstName: 'John',
+ LastName: 'Heart',
+ Position: 'CEO',
+ BirthDate: new Date('1964/03/16'),
+ HireDate: new Date('1995/01/15'),
+ City: 'Los Angeles',
+ State: 'CA',
+ Email: 'jheart@dx-email.com',
+ Phone: '+1(213) 555-9392'
+ },
+ {
+ ID: 2,
+ FirstName: 'Olivia',
+ LastName: 'Peyton',
+ Position: 'Sales Assistant',
+ BirthDate: new Date('1981/06/03'),
+ HireDate: new Date('2012/05/14'),
+ City: 'Atlanta',
+ State: 'GA',
+ Email: 'oliviap@dx-email.com',
+ Phone: '+1(310) 555-2728'
+ },
+ {
+ ID: 3,
+ FirstName: 'Robert',
+ LastName: 'Reagan',
+ Position: 'CMO',
+ BirthDate: new Date('1974/09/07'),
+ HireDate: new Date('2002/11/08'),
+ City: 'Bentonville',
+ State: 'AR',
+ Email: 'robertr@dx-email.com',
+ Phone: '+1(818) 555-2387'
+ },
+];
+
+export const SmartPaste: Story = {
+ argTypes: {
+ editingMode: {
+ control: 'radio',
+ options: ['form', 'popup'],
+ description: 'Editing mode for the DataGrid',
+ },
+ },
+ args: {
+ dataSource: employees,
+ keyExpr: 'ID',
+ showBorders: true,
+ editingMode: "popup",
+ },
+ render: (args) => {
+ const { editingMode, ...gridArgs } = args;
+
+ const sampleText = `Name: Sarah Johnson
+Position: Senior Developer
+Email: sarah.johnson@company.com
+Phone: +1(555) 123-4567
+Born: 1985/06/15
+Hired: 2020/03/01
+Location: San Francisco, CA`;
+
+ const copyToClipboard = () => {
+ navigator.clipboard.writeText(sampleText).then(() => {
+ notify({
+ message: 'Text copied to clipboard! Now click "Add" and then "Smart Paste" in the form.',
+ position: {
+ my: 'bottom center',
+ at: 'bottom',
+ of: window,
+ },
+ width: 'auto',
+ }, 'success', 2500);
+ }).catch(() => {
+ notify({
+ message: 'Failed to copy text to clipboard',
+ position: {
+ my: 'bottom center',
+ at: 'bottom',
+ of: window,
+ },
+ width: 'auto',
+ }, 'error', 2000);
+ });
+ };
+
+ return (
+
+
+
AI Integration - Smart Paste Demo
+
How to use:
+
+ - Change editing mode in Storybook Controls: form (inline) or popup (modal)
+ - Click the "Copy Sample Text" button below
+ - Click "Add" button in the DataGrid to open the edit form
+ - In the edit form, click the "Smart Paste" button
+ - Watch the AI automatically fill all fields from the copied text! ✨
+
+
+ Sample text:
+
+
+ {sampleText}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+};
diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing_form_smart_paste.test.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing_form_smart_paste.test.ts
new file mode 100644
index 000000000000..15abc30e8d61
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing_form_smart_paste.test.ts
@@ -0,0 +1,487 @@
+import {
+ afterEach, beforeEach, describe, expect, it, jest,
+} from '@jest/globals';
+import $ from '@js/core/renderer';
+import DataGrid from '@js/ui/data_grid';
+import { AIIntegration } from '@ts/core/ai_integration/core/ai_integration';
+
+const GRID_CONTAINER_ID = 'gridContainer';
+
+interface RequestResult {
+ promise: Promise;
+ abort: () => void;
+}
+
+const dataSource = [{
+ ID: 1,
+ FirstName: 'John',
+ LastName: 'Heart',
+ Position: 'CEO',
+ BirthDate: new Date('1964/03/16'),
+ HireDate: new Date('1995/01/15'),
+ City: 'Los Angeles',
+ State: 'CA',
+ Email: 'john.heart@example.com',
+ Phone: '555-0100',
+}, {
+ ID: 2,
+ FirstName: 'Olivia',
+ LastName: 'Peyton',
+ Position: 'Sales Assistant',
+ BirthDate: new Date('1981/06/03'),
+ HireDate: new Date('2012/05/14'),
+ City: 'San Diego',
+ State: 'CA',
+ Email: 'olivia.peyton@example.com',
+ Phone: '555-0200',
+}];
+
+const flushAsync = async (): Promise => {
+ jest.runOnlyPendingTimers();
+ await Promise.resolve();
+};
+
+const getEditorValue = (editForm: any, dataField: string): any => {
+ const $formElement = editForm.$element();
+ const itemID = editForm.getItemID(dataField);
+ const escapedID = itemID.replace(/([.:!])/g, '\\$1');
+ const $input = $formElement.find(`#${escapedID}`);
+ if ($input.length) {
+ const $widget = $input.closest('.dx-widget');
+ if ($widget.length) {
+ const widgetNames = $widget.data('dxComponents');
+ if (widgetNames && widgetNames.length > 0) {
+ const widget = $widget.data(widgetNames[0]);
+ return widget?.option('value');
+ }
+ }
+ }
+ return undefined;
+};
+
+describe('DataGrid - Form-based editing with Smart Paste', () => {
+ let $gridContainer;
+ let gridInstance: DataGrid;
+
+ beforeEach(() => {
+ jest.useFakeTimers();
+ $gridContainer = $('')
+ .attr('id', GRID_CONTAINER_ID)
+ .appendTo(document.body);
+
+ Object.defineProperty(navigator, 'clipboard', {
+ value: {
+ readText: jest.fn<() => Promise
>().mockResolvedValue('Jane Doe, CTO, San Francisco, jane.doe@example.com, 555-9999'),
+ } as Partial,
+ writable: true,
+ configurable: true,
+ });
+ });
+
+ afterEach(() => {
+ gridInstance?.dispose();
+ $gridContainer.remove();
+ jest.useRealTimers();
+ });
+
+ it('should update editors when Smart Paste is triggered', async () => {
+ const mockAIResultString = 'FirstName:::Jane;;;LastName:::Doe;;;Position:::CTO;;;City:::San Francisco;;;Email:::jane.doe@example.com;;;Phone:::555-9999';
+
+ gridInstance = new DataGrid($gridContainer.get(0) as HTMLDivElement, {
+ dataSource: [...dataSource],
+ keyExpr: 'ID',
+ editing: {
+ mode: 'form',
+ allowUpdating: true,
+ form: {
+ aiIntegration: new AIIntegration({
+ sendRequest(): RequestResult {
+ return {
+ promise: Promise.resolve(mockAIResultString),
+ abort: (): void => {},
+ };
+ },
+ }),
+ },
+ },
+ columns: ['FirstName', 'LastName', 'Position', 'City', 'Email', 'Phone'],
+ });
+
+ await flushAsync();
+
+ gridInstance.editRow(0);
+ await flushAsync();
+
+ const editForm = (gridInstance as any).getController('editing')._editForm;
+ expect(editForm).toBeDefined();
+
+ const formAIIntegration = editForm.option('aiIntegration');
+ expect(formAIIntegration).toBeDefined();
+
+ const $formElement = editForm.$element();
+ const $buttonsContainer = $formElement.parent().find('.dx-datagrid-form-buttons-container');
+ const $allButtons = $buttonsContainer.find('.dx-button');
+
+ const $smartPasteButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Smart Paste';
+ }).first();
+
+ expect($smartPasteButton.length).toBe(1);
+
+ const dataController = (gridInstance as any).getController('data');
+ const fireErrorSpy = jest.spyOn(dataController, 'fireError');
+
+ ($smartPasteButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ expect(fireErrorSpy).not.toHaveBeenCalledWith('E1043');
+ fireErrorSpy.mockRestore();
+
+ expect(getEditorValue(editForm, 'FirstName')).toBe('Jane');
+ expect(getEditorValue(editForm, 'LastName')).toBe('Doe');
+ expect(getEditorValue(editForm, 'Position')).toBe('CTO');
+ expect(getEditorValue(editForm, 'City')).toBe('San Francisco');
+ expect(getEditorValue(editForm, 'Email')).toBe('jane.doe@example.com');
+ expect(getEditorValue(editForm, 'Phone')).toBe('555-9999');
+
+ const editingController = (gridInstance as any).getController('editing');
+ const changes = editingController.getChanges();
+ expect(changes.length).toBe(1);
+ expect(changes[0].data.FirstName).toBe('Jane');
+ expect(changes[0].data.LastName).toBe('Doe');
+ expect(changes[0].data.Position).toBe('CTO');
+
+ expect(changes[0].key).toBe(1);
+ expect(changes[0].type).toBe('update');
+
+ const $saveButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Save';
+ }).first();
+
+ expect($saveButton.length).toBe(1);
+ ($saveButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ const $gridElement = $gridContainer;
+ const $firstNameCell = $gridElement.find('.dx-data-row').first().find('td').eq(0);
+ const $lastNameCell = $gridElement.find('.dx-data-row').first().find('td').eq(1);
+ const $positionCell = $gridElement.find('.dx-data-row').first().find('td').eq(2);
+
+ expect($firstNameCell.text()).toBe('Jane');
+ expect($lastNameCell.text()).toBe('Doe');
+ expect($positionCell.text()).toBe('CTO');
+ });
+
+ it('should not save changes when Cancel button is clicked after Smart Paste', async () => {
+ const mockAIResultString = 'FirstName:::Alice;;;LastName:::Brown;;;Position:::Manager;;;City:::New York;;;Email:::alice.brown@example.com;;;Phone:::555-3333';
+
+ gridInstance = new DataGrid($gridContainer.get(0) as HTMLDivElement, {
+ dataSource: [...dataSource],
+ keyExpr: 'ID',
+ editing: {
+ mode: 'form',
+ allowUpdating: true,
+ form: {
+ aiIntegration: new AIIntegration({
+ sendRequest(): RequestResult {
+ return {
+ promise: Promise.resolve(mockAIResultString),
+ abort: (): void => {},
+ };
+ },
+ }),
+ },
+ },
+ columns: ['FirstName', 'LastName', 'Position', 'City', 'Email', 'Phone'],
+ });
+
+ await flushAsync();
+
+ gridInstance.editRow(1);
+ await flushAsync();
+
+ const editForm = (gridInstance as any).getController('editing')._editForm;
+ expect(editForm).toBeDefined();
+
+ const $formElement = editForm.$element();
+ const $buttonsContainer = $formElement.parent().find('.dx-datagrid-form-buttons-container');
+ const $allButtons = $buttonsContainer.find('.dx-button');
+
+ const $smartPasteButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Smart Paste';
+ }).first();
+
+ expect($smartPasteButton.length).toBe(1);
+
+ ($smartPasteButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ expect(getEditorValue(editForm, 'FirstName')).toBe('Alice');
+ expect(getEditorValue(editForm, 'LastName')).toBe('Brown');
+
+ const $cancelButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Cancel';
+ }).first();
+
+ expect($cancelButton.length).toBe(1);
+ ($cancelButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ const editingController = (gridInstance as any).getController('editing');
+ const changes = editingController.getChanges();
+ expect(changes.length).toBe(0);
+
+ const $gridElement = $gridContainer;
+ const $secondRow = $gridElement.find('.dx-data-row').eq(1);
+ const $firstNameCell = $secondRow.find('td').eq(0);
+ const $lastNameCell = $secondRow.find('td').eq(1);
+ const $positionCell = $secondRow.find('td').eq(2);
+
+ expect($firstNameCell.text()).toBe('Olivia');
+ expect($lastNameCell.text()).toBe('Peyton');
+ expect($positionCell.text()).toBe('Sales Assistant');
+ });
+
+ it('should update editors when Smart Paste is triggered in popup mode', async () => {
+ const mockAIResultString = 'FirstName:::Jane;;;LastName:::Doe;;;Position:::CTO;;;City:::San Francisco;;;Email:::jane.doe@example.com;;;Phone:::555-9999';
+
+ gridInstance = new DataGrid($gridContainer.get(0) as HTMLDivElement, {
+ dataSource: [...dataSource],
+ keyExpr: 'ID',
+ editing: {
+ mode: 'popup',
+ allowUpdating: true,
+ form: {
+ aiIntegration: new AIIntegration({
+ sendRequest(): RequestResult {
+ return {
+ promise: Promise.resolve(mockAIResultString),
+ abort: (): void => {},
+ };
+ },
+ }),
+ },
+ },
+ columns: ['FirstName', 'LastName', 'Position', 'City', 'Email', 'Phone'],
+ });
+
+ await flushAsync();
+
+ gridInstance.editRow(0);
+ await flushAsync();
+
+ const editForm = (gridInstance as any).getController('editing')._editForm;
+ expect(editForm).toBeDefined();
+
+ const formAIIntegration = editForm.option('aiIntegration');
+ expect(formAIIntegration).toBeDefined();
+
+ const editingController = (gridInstance as any).getController('editing');
+ const $popupContent = $(editingController.getPopupContent());
+ expect($popupContent.length).toBeGreaterThan(0);
+
+ const $overlayContent = $popupContent.closest('.dx-overlay-content');
+ const $allButtons = $overlayContent.find('.dx-button');
+ // @ts-expect-error jQuery filter accepts function but types are incorrect
+ const $smartPasteButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Smart Paste';
+ }).first();
+
+ expect($smartPasteButton.length).toBe(1);
+
+ const dataController = (gridInstance as any).getController('data');
+ const fireErrorSpy = jest.spyOn(dataController, 'fireError');
+
+ ($smartPasteButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ expect(fireErrorSpy).not.toHaveBeenCalledWith('E1043');
+ fireErrorSpy.mockRestore();
+
+ expect(getEditorValue(editForm, 'FirstName')).toBe('Jane');
+ expect(getEditorValue(editForm, 'LastName')).toBe('Doe');
+ expect(getEditorValue(editForm, 'Position')).toBe('CTO');
+ expect(getEditorValue(editForm, 'City')).toBe('San Francisco');
+ expect(getEditorValue(editForm, 'Email')).toBe('jane.doe@example.com');
+ expect(getEditorValue(editForm, 'Phone')).toBe('555-9999');
+
+ const changes = editingController.getChanges();
+ expect(changes.length).toBe(1);
+ expect(changes[0].data.FirstName).toBe('Jane');
+ expect(changes[0].data.LastName).toBe('Doe');
+ expect(changes[0].data.Position).toBe('CTO');
+
+ expect(changes[0].key).toBe(1);
+ expect(changes[0].type).toBe('update');
+ });
+
+ it('should save changes when Save button is clicked after Smart Paste in popup mode', async () => {
+ const mockAIResultString = 'FirstName:::Alice;;;LastName:::Smith;;;Position:::Manager;;;City:::New York;;;Email:::alice.smith@example.com;;;Phone:::555-7777';
+
+ gridInstance = new DataGrid($gridContainer.get(0) as HTMLDivElement, {
+ dataSource: [...dataSource],
+ keyExpr: 'ID',
+ editing: {
+ mode: 'popup',
+ allowUpdating: true,
+ form: {
+ aiIntegration: new AIIntegration({
+ sendRequest(): RequestResult {
+ return {
+ promise: Promise.resolve(mockAIResultString),
+ abort: (): void => {},
+ };
+ },
+ }),
+ },
+ },
+ columns: ['FirstName', 'LastName', 'Position', 'City', 'Email', 'Phone'],
+ });
+
+ await flushAsync();
+
+ gridInstance.editRow(0);
+ await flushAsync();
+
+ const editForm = (gridInstance as any).getController('editing')._editForm;
+ expect(editForm).toBeDefined();
+
+ const editingController = (gridInstance as any).getController('editing');
+ const $popupContent = $(editingController.getPopupContent());
+ expect($popupContent.length).toBeGreaterThan(0);
+
+ const $overlayContent = $popupContent.closest('.dx-overlay-content');
+ const $allButtons = $overlayContent.find('.dx-button');
+ // @ts-expect-error jQuery filter accepts function but types are incorrect
+ const $smartPasteButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Smart Paste';
+ }).first();
+
+ expect($smartPasteButton.length).toBe(1);
+
+ ($smartPasteButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ expect(getEditorValue(editForm, 'FirstName')).toBe('Alice');
+ expect(getEditorValue(editForm, 'LastName')).toBe('Smith');
+ expect(getEditorValue(editForm, 'Position')).toBe('Manager');
+
+ // @ts-expect-error jQuery filter accepts function but types are incorrect
+ const $saveButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Save';
+ }).first();
+
+ expect($saveButton.length).toBe(1);
+ ($saveButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ const $gridElement = $gridContainer;
+ const $firstNameCell = $gridElement.find('.dx-data-row').first().find('td').eq(0);
+ const $lastNameCell = $gridElement.find('.dx-data-row').first().find('td').eq(1);
+ const $positionCell = $gridElement.find('.dx-data-row').first().find('td').eq(2);
+
+ expect($firstNameCell.text()).toBe('Alice');
+ expect($lastNameCell.text()).toBe('Smith');
+ expect($positionCell.text()).toBe('Manager');
+ });
+
+ it('should not save changes when Cancel button is clicked after Smart Paste in popup mode', async () => {
+ const mockAIResultString = 'FirstName:::Bob;;;LastName:::Johnson;;;Position:::Developer;;;City:::Seattle;;;Email:::bob.johnson@example.com;;;Phone:::555-8888';
+
+ gridInstance = new DataGrid($gridContainer.get(0) as HTMLDivElement, {
+ dataSource: [...dataSource],
+ keyExpr: 'ID',
+ editing: {
+ mode: 'popup',
+ allowUpdating: true,
+ form: {
+ aiIntegration: new AIIntegration({
+ sendRequest(): RequestResult {
+ return {
+ promise: Promise.resolve(mockAIResultString),
+ abort: (): void => {},
+ };
+ },
+ }),
+ },
+ },
+ columns: ['FirstName', 'LastName', 'Position', 'City', 'Email', 'Phone'],
+ });
+
+ await flushAsync();
+
+ gridInstance.editRow(1);
+ await flushAsync();
+
+ const editForm = (gridInstance as any).getController('editing')._editForm;
+ expect(editForm).toBeDefined();
+
+ const editingController = (gridInstance as any).getController('editing');
+ const $popupContent = $(editingController.getPopupContent());
+ expect($popupContent.length).toBeGreaterThan(0);
+
+ const $overlayContent = $popupContent.closest('.dx-overlay-content');
+ const $allButtons = $overlayContent.find('.dx-button');
+ // @ts-expect-error jQuery filter accepts function but types are incorrect
+ const $smartPasteButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Smart Paste';
+ }).first();
+
+ expect($smartPasteButton.length).toBe(1);
+
+ ($smartPasteButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ expect(getEditorValue(editForm, 'FirstName')).toBe('Bob');
+ expect(getEditorValue(editForm, 'LastName')).toBe('Johnson');
+
+ // @ts-expect-error jQuery filter accepts function but types are incorrect
+ const $cancelButton = $allButtons.filter((_, el) => {
+ const $el = $(el);
+ const buttonInstance = ($el as any).dxButton('instance');
+ return buttonInstance?.option('text') === 'Cancel';
+ }).first();
+
+ expect($cancelButton.length).toBe(1);
+ ($cancelButton.get(0) as HTMLElement)?.click();
+
+ await flushAsync();
+
+ const changes = editingController.getChanges();
+ expect(changes.length).toBe(0);
+
+ const $gridElement = $gridContainer;
+ const $secondRow = $gridElement.find('.dx-data-row').eq(1);
+ const $firstNameCell = $secondRow.find('td').eq(0);
+ const $lastNameCell = $secondRow.find('td').eq(1);
+ const $positionCell = $secondRow.find('td').eq(2);
+
+ expect($firstNameCell.text()).toBe('Olivia');
+ expect($lastNameCell.text()).toBe('Peyton');
+ expect($positionCell.text()).toBe('Sales Assistant');
+ });
+});
diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts
index 05a530e0f618..861aedaf1ed6 100644
--- a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts
+++ b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts
@@ -135,7 +135,6 @@ const editingControllerExtender = (Base: ModuleType) => class
}
}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected _updateEditRowCore(row, skipCurrentRow, isCustomSetCellValue) {
const editForm = this._editForm;
@@ -155,22 +154,44 @@ const editingControllerExtender = (Base: ModuleType) => class
}
}
+ protected _getSmartPasteButtonConfig() {
+ return {
+ text: 'Smart Paste',
+ icon: 'clipboardpastesparkle',
+ onClick: () => {
+ this._editForm?.smartPaste();
+ },
+ };
+ }
+
protected _showEditPopup(rowIndex, repaintForm?) {
const isMobileDevice = devices.current().deviceType !== 'desktop';
const editPopupClass = this.addWidgetPrefix(EDIT_POPUP_CLASS);
+ const toolbarItems = [
+ {
+ toolbar: 'bottom', location: 'after', widget: 'dxButton', options: this._getSaveButtonConfig(),
+ },
+ {
+ toolbar: 'bottom', location: 'after', widget: 'dxButton', options: this._getCancelButtonConfig(),
+ },
+ ];
+
+ const editFormOptions = this.option(EDITING_FORM_OPTION_NAME);
+ if (editFormOptions?.aiIntegration) {
+ toolbarItems.unshift({
+ toolbar: 'bottom',
+ location: 'before',
+ widget: 'dxButton',
+ options: this._getSmartPasteButtonConfig(),
+ });
+ }
+
const popupOptions = extend(
{
showTitle: false,
fullScreen: isMobileDevice,
wrapperAttr: { class: editPopupClass },
- toolbarItems: [
- {
- toolbar: 'bottom', location: 'after', widget: 'dxButton', options: this._getSaveButtonConfig(),
- },
- {
- toolbar: 'bottom', location: 'after', widget: 'dxButton', options: this._getCancelButtonConfig(),
- },
- ],
+ toolbarItems,
contentTemplate: this._getPopupEditFormTemplate(rowIndex),
},
this.option(EDITING_POPUP_OPTION_NAME),
@@ -368,6 +389,7 @@ const editingControllerExtender = (Base: ModuleType) => class
const editFormItemClass = this.addWidgetPrefix(EDIT_FORM_ITEM_CLASS);
let items: any = this.option('editing.form.items');
const isCustomEditorType = {};
+ const formAIIntegration = this.option('editing.form.aiIntegration');
if (!items) {
const columns = this._columnsController.getColumns();
@@ -394,6 +416,49 @@ const editingControllerExtender = (Base: ModuleType) => class
return extend({}, editFormOptions, {
items,
formID: `dx-${new Guid()}`,
+ aiIntegration: formAIIntegration,
+ onSmartPasted: (e) => {
+ if (e.aiResult && this._editForm) {
+ Object.keys(e.aiResult).forEach((dataField) => {
+ const newValue = e.aiResult[dataField];
+
+ const column = this._columnsController.columnOption(dataField);
+ if (column) {
+ const cellOptions = {
+ data: detailOptions.data,
+ column,
+ row: { data: detailOptions.data },
+ columnIndex: column.index,
+ key: detailOptions.key,
+ };
+
+ this.updateFieldValue(cellOptions, newValue, undefined);
+ }
+
+ const itemID = this._editForm.getItemID(dataField);
+ const $formElement = this._editForm.$element();
+
+ const escapedID = itemID.replace(/([.:!])/g, '\\$1');
+ const $fieldItem = $formElement.find(`#${escapedID}`).first();
+
+ if ($fieldItem.length) {
+ const $widget = $fieldItem.closest('.dx-widget');
+
+ if ($widget.length) {
+ const widgetNames = $widget.data('dxComponents');
+
+ if (widgetNames && widgetNames.length > 0) {
+ const widgetInstance = $widget.data(widgetNames[0]);
+
+ if (widgetInstance && widgetInstance.option) {
+ widgetInstance.option('value', newValue);
+ }
+ }
+ }
+ }
+ });
+ }
+ },
customizeItem: (item) => {
let column;
const itemId = item.name || item.dataField;
@@ -448,6 +513,11 @@ const editingControllerExtender = (Base: ModuleType) => class
if (!isPopupForm) {
const $buttonsContainer = $('').addClass(this.addWidgetPrefix(FORM_BUTTONS_CONTAINER_CLASS)).appendTo($container);
+
+ if (editFormOptions?.aiIntegration) {
+ this._createComponent($('
').appendTo($buttonsContainer), Button, this._getSmartPasteButtonConfig());
+ }
+
this._createComponent($('
').appendTo($buttonsContainer), Button, this._getSaveButtonConfig());
this._createComponent($('
').appendTo($buttonsContainer), Button, this._getCancelButtonConfig());
}