From c4f5802484e9f4c7f933ee129139adce448181a9 Mon Sep 17 00:00:00 2001 From: Andrei_Ser Date: Mon, 22 Jun 2026 20:18:10 +0400 Subject: [PATCH 1/3] add DataGrid semantic search demo --- .../Angular/app/app.component.html | 31 ++++++ .../Angular/app/app.component.ts | 78 ++++++++++++++ .../SemanticSearch/Angular/index.html | 26 +++++ .../DataGrid/SemanticSearch/React/App.tsx | 83 +++++++++++++++ .../DataGrid/SemanticSearch/React/index.html | 24 +++++ .../DataGrid/SemanticSearch/React/index.tsx | 9 ++ .../DataGrid/SemanticSearch/React/styles.css | 3 + .../DataGrid/SemanticSearch/ReactJs/App.js | 100 ++++++++++++++++++ .../SemanticSearch/ReactJs/index.html | 44 ++++++++ .../DataGrid/SemanticSearch/ReactJs/index.js | 5 + .../SemanticSearch/ReactJs/styles.css | 3 + .../Demos/DataGrid/SemanticSearch/Vue/App.vue | 95 +++++++++++++++++ .../DataGrid/SemanticSearch/Vue/index.html | 29 +++++ .../DataGrid/SemanticSearch/Vue/index.ts | 4 + .../DataGrid/SemanticSearch/jQuery/index.html | 19 ++++ .../DataGrid/SemanticSearch/jQuery/index.js | 71 +++++++++++++ 16 files changed, 624 insertions(+) create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.html create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Angular/index.html create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/React/index.html create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/React/index.tsx create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/React/styles.css create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/App.js create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.html create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.js create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/styles.css create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Vue/App.vue create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.html create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.ts create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.html b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.html new file mode 100644 index 000000000000..4ec9bfddc3e3 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.html @@ -0,0 +1,31 @@ + + + + + +
+ +
+
+ +
+ + + +
diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts new file mode 100644 index 000000000000..6d1c07040fe4 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts @@ -0,0 +1,78 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { Component, ViewChild, enableProdMode, provideZoneChangeDetection } from '@angular/core'; +import { DxDataGridComponent, DxDataGridModule, DxDataGridTypes } from 'devextreme-angular/ui/data-grid'; +import { DxNumberBoxModule, DxNumberBoxTypes } from 'devextreme-angular/ui/number-box'; +import * as AspNetData from 'devextreme-aspnet-data-nojquery'; + +if (!/localhost/.test(document.location.host)) { + enableProdMode(); +} + +let modulePrefix = ''; +// @ts-ignore +if (window && window.config?.packageConfigPaths) { + modulePrefix = '/app'; +} + +const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridSemanticSearch'; + +@Component({ + selector: 'demo-app', + templateUrl: `.${modulePrefix}/app.component.html`, + imports: [ + DxDataGridModule, + DxNumberBoxModule, + ], +}) +export class AppComponent { + @ViewChild(DxDataGridComponent, { static: false }) dataGrid: DxDataGridComponent; + + searchValue = ''; + + similarityFactor = 0.31; + + dataSource: AspNetData.CustomStore; + + constructor() { + this.dataSource = AspNetData.createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + loadParams: { + searchValue: () => this.searchValue, + similarityFactor: () => this.similarityFactor, + }, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }); + } + + onSimilarityFactorChanged(e: DxNumberBoxTypes.ValueChangedEvent): void { + this.similarityFactor = e.value; + if (this.searchValue !== '') { + this.dataGrid.instance.getDataSource().reload(); + } + } + + onEditorPreparing(e: DxDataGridTypes.EditorPreparingEvent): void { + if (e.parentType === 'searchPanel') { + let searchTimeout: ReturnType | undefined; + e.editorOptions.onValueChanged = (args: { value: string }) => { + if (searchTimeout) { + clearTimeout(searchTimeout); + } + searchTimeout = setTimeout(() => { + this.searchValue = args.value; + e.component.getDataSource().reload(); + }, e.updateValueTimeout); + }; + e.editorOptions.placeholder = 'Try: clothing'; + } + } +} + +bootstrapApplication(AppComponent, { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), + ], +}); diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Angular/index.html b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/index.html new file mode 100644 index 000000000000..1ab1fb54a1df --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/index.html @@ -0,0 +1,26 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + + + +
+ Loading... +
+ + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx b/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx new file mode 100644 index 000000000000..a1950cfbd449 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx @@ -0,0 +1,83 @@ +import React, { useCallback, useRef, useMemo } from 'react'; +import DataGrid, { Column, Scrolling, SearchPanel, Toolbar, Item, DataGridRef, DataGridTypes } from 'devextreme-react/data-grid'; +import NumberBox, { NumberBoxTypes } from 'devextreme-react/number-box'; +import { createStore } from 'devextreme-aspnet-data-nojquery'; + +const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridSemanticSearch'; + +const App = () => { + const searchValueRef = useRef(''); + const similarityFactorRef = useRef(0.31); + + const gridRef = useRef(null); + + const store = useMemo(() => createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + ajaxOptions.data = { + ...ajaxOptions.data, + searchValue: searchValueRef.current, + similarityFactor: similarityFactorRef.current, + }; + }, + }), []); + + const onSimilarityFactorChanged = useCallback(({ value }: NumberBoxTypes.ValueChangedEvent) => { + similarityFactorRef.current = value; + if (searchValueRef.current !== '') { + gridRef.current?.instance().getDataSource().reload(); + } + }, []); + + const onEditorPreparing = useCallback((e: DataGridTypes.EditorPreparingEvent) => { + if (e.parentType === 'searchPanel') { + let searchTimeout: ReturnType | undefined; + e.editorOptions.onValueChanged = (args: { value: string }) => { + if (searchTimeout) { + clearTimeout(searchTimeout); + } + searchTimeout = setTimeout(() => { + searchValueRef.current = args.value; + e.component.getDataSource().reload(); + }, e.updateValueTimeout); + }; + e.editorOptions.placeholder = 'Try: clothing'; + } + }, []); + + return ( + + + + + + + + + + + + + + ); +}; + +export default App; diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/React/index.html b/apps/demos/Demos/DataGrid/SemanticSearch/React/index.html new file mode 100644 index 000000000000..ee451f8288ff --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/React/index.html @@ -0,0 +1,24 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + +
+
+
+ + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/React/index.tsx b/apps/demos/Demos/DataGrid/SemanticSearch/React/index.tsx new file mode 100644 index 000000000000..8acbec4b6179 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/React/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './App.tsx'; + +ReactDOM.render( + , + document.getElementById('app'), +); diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/React/styles.css b/apps/demos/Demos/DataGrid/SemanticSearch/React/styles.css new file mode 100644 index 000000000000..dc6a288e93e4 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/React/styles.css @@ -0,0 +1,3 @@ +.align-bottom.dx-toolbar-item { + vertical-align: bottom; +} diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/App.js b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/App.js new file mode 100644 index 000000000000..2a16ed848075 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/App.js @@ -0,0 +1,100 @@ +import React, { useCallback, useRef, useMemo } from 'react'; +import DataGrid, { + Column, + Scrolling, + SearchPanel, + Toolbar, + Item, +} from 'devextreme-react/data-grid'; +import NumberBox from 'devextreme-react/number-box'; +import { createStore } from 'devextreme-aspnet-data-nojquery'; +const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridSemanticSearch'; +const App = () => { + const searchValueRef = useRef(''); + const similarityFactorRef = useRef(0.31); + const gridRef = useRef(null); + const store = useMemo( + () => + createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + ajaxOptions.data = { + ...ajaxOptions.data, + searchValue: searchValueRef.current, + similarityFactor: similarityFactorRef.current, + }; + }, + }), + [], + ); + const onSimilarityFactorChanged = useCallback(({ value }) => { + similarityFactorRef.current = value; + if (searchValueRef.current !== '') { + gridRef.current?.instance().getDataSource().reload(); + } + }, []); + const onEditorPreparing = useCallback((e) => { + if (e.parentType === 'searchPanel') { + let searchTimeout; + e.editorOptions.onValueChanged = (args) => { + if (searchTimeout) { + clearTimeout(searchTimeout); + } + searchTimeout = setTimeout(() => { + searchValueRef.current = args.value; + e.component.getDataSource().reload(); + }, e.updateValueTimeout); + }; + e.editorOptions.placeholder = 'Try: clothing'; + } + }, []); + return ( + + + + + + + + + + + + + + ); +}; +export default App; diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.html b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.html new file mode 100644 index 000000000000..db31b0fd60c6 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.html @@ -0,0 +1,44 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + +
+
+
+ + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.js b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.js new file mode 100644 index 000000000000..b853e0be8242 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/index.js @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App.js'; + +ReactDOM.render(, document.getElementById('app')); diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/styles.css b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/styles.css new file mode 100644 index 000000000000..dc6a288e93e4 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/ReactJs/styles.css @@ -0,0 +1,3 @@ +.align-bottom.dx-toolbar-item { + vertical-align: bottom; +} diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Vue/App.vue b/apps/demos/Demos/DataGrid/SemanticSearch/Vue/App.vue new file mode 100644 index 000000000000..56f318e7d296 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Vue/App.vue @@ -0,0 +1,95 @@ + + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.html b/apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.html new file mode 100644 index 000000000000..b21c5e373a7a --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.html @@ -0,0 +1,29 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + + +
+
+
+ + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.ts b/apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.ts new file mode 100644 index 000000000000..684d04215d72 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Vue/index.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html new file mode 100644 index 000000000000..5aa7314c6053 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html @@ -0,0 +1,19 @@ + + + + DevExtreme Demo + + + + + + + + + + +
+
+
+ + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js new file mode 100644 index 000000000000..7c21c9651955 --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js @@ -0,0 +1,71 @@ +$(() => { + const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridSemanticSearch'; + + let searchValue = ''; + let similarityFactor = 0.31; + + const dataGrid = $('#gridContainer').dxDataGrid({ + dataSource: DevExpress.data.AspNet.createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + loadParams: { + searchValue: () => searchValue, + similarityFactor: () => similarityFactor, + }, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }), + showBorders: true, + remoteOperations: true, + height: 600, + columns: ['ID', 'Name', 'Description'], + scrolling: { + mode: 'virtual', + }, + searchPanel: { + visible: true, + }, + toolbar: { + items: [ + { + location: 'after', + cssClass: 'align-bottom', + widget: 'dxNumberBox', + options: { + label: 'Similarity Factor', + labelMode: 'floating', + value: similarityFactor, + min: 0, + max: 1, + format: '0.00', + step: 0.05, + onValueChanged(e) { + similarityFactor = e.value; + if (searchValue !== '') { + dataGrid.getDataSource().reload(); + } + }, + }, + }, + { + name: 'searchPanel', + cssClass: 'align-bottom', + }, + ], + }, + onEditorPreparing(e) { + if (e.parentType === 'searchPanel') { + let searchTimeout; + e.editorOptions.onValueChanged = (args) => { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + searchValue = args.value; + e.component.getDataSource().reload(); + }, e.updateValueTimeout); + }; + e.editorOptions.placeholder = 'Try: clothing'; + } + }, + }).dxDataGrid('instance'); +}); From aef9b6cb3052d9c60434e4a0534d9b2285ee28a4 Mon Sep 17 00:00:00 2001 From: Andrei_Ser Date: Tue, 23 Jun 2026 14:39:34 +0400 Subject: [PATCH 2/3] post-review fixes, menumeta merge --- .../Angular/app/app.component.css | 4 ++ .../Angular/app/app.component.ts | 42 +++++++-------- .../DataGrid/SemanticSearch/React/App.tsx | 39 +++++++------- .../DataGrid/SemanticSearch/React/styles.css | 2 +- .../DataGrid/SemanticSearch/ReactJs/App.js | 37 +++++++------- .../SemanticSearch/ReactJs/styles.css | 2 +- .../Demos/DataGrid/SemanticSearch/Vue/App.vue | 37 +++++++------- .../DataGrid/SemanticSearch/jQuery/index.html | 1 + .../DataGrid/SemanticSearch/jQuery/index.js | 35 +++++++------ .../DataGrid/SemanticSearch/jQuery/styles.css | 3 ++ apps/demos/menuMeta.json | 51 +++++++++++++++++++ 11 files changed, 157 insertions(+), 96 deletions(-) create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.css create mode 100644 apps/demos/Demos/DataGrid/SemanticSearch/jQuery/styles.css diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.css b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.css new file mode 100644 index 000000000000..4a7b8ff8058c --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.css @@ -0,0 +1,4 @@ +:host .align-bottom.dx-toolbar-item { + vertical-align: bottom; +} + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts index 6d1c07040fe4..8c83cad4c5e2 100644 --- a/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts +++ b/apps/demos/Demos/DataGrid/SemanticSearch/Angular/app/app.component.ts @@ -1,7 +1,8 @@ import { bootstrapApplication } from '@angular/platform-browser'; -import { Component, ViewChild, enableProdMode, provideZoneChangeDetection } from '@angular/core'; -import { DxDataGridComponent, DxDataGridModule, DxDataGridTypes } from 'devextreme-angular/ui/data-grid'; -import { DxNumberBoxModule, DxNumberBoxTypes } from 'devextreme-angular/ui/number-box'; +import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core'; +import { DxDataGridModule, type DxDataGridTypes } from 'devextreme-angular/ui/data-grid'; +import { DxNumberBoxModule, type DxNumberBoxTypes } from 'devextreme-angular/ui/number-box'; +import DataSource from 'devextreme/data/data_source'; import * as AspNetData from 'devextreme-aspnet-data-nojquery'; if (!/localhost/.test(document.location.host)) { @@ -19,38 +20,39 @@ const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridSemanticSearch' @Component({ selector: 'demo-app', templateUrl: `.${modulePrefix}/app.component.html`, + styleUrls: [`.${modulePrefix}/app.component.css`], imports: [ DxDataGridModule, DxNumberBoxModule, ], }) export class AppComponent { - @ViewChild(DxDataGridComponent, { static: false }) dataGrid: DxDataGridComponent; - searchValue = ''; similarityFactor = 0.31; - dataSource: AspNetData.CustomStore; + dataSource: DataSource; constructor() { - this.dataSource = AspNetData.createStore({ - key: 'ID', - loadUrl: `${url}/Get`, - loadParams: { - searchValue: () => this.searchValue, - similarityFactor: () => this.similarityFactor, - }, - onBeforeSend: (method, ajaxOptions) => { - ajaxOptions.xhrFields = { withCredentials: true }; - }, + this.dataSource = new DataSource({ + store: AspNetData.createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + loadParams: { + searchValue: () => this.searchValue, + similarityFactor: () => this.similarityFactor, + }, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }) }); } onSimilarityFactorChanged(e: DxNumberBoxTypes.ValueChangedEvent): void { this.similarityFactor = e.value; if (this.searchValue !== '') { - this.dataGrid.instance.getDataSource().reload(); + this.dataSource.reload(); } } @@ -58,12 +60,10 @@ export class AppComponent { if (e.parentType === 'searchPanel') { let searchTimeout: ReturnType | undefined; e.editorOptions.onValueChanged = (args: { value: string }) => { - if (searchTimeout) { - clearTimeout(searchTimeout); - } + clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { this.searchValue = args.value; - e.component.getDataSource().reload(); + this.dataSource.reload(); }, e.updateValueTimeout); }; e.editorOptions.placeholder = 'Try: clothing'; diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx b/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx index a1950cfbd449..88816005df69 100644 --- a/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx +++ b/apps/demos/Demos/DataGrid/SemanticSearch/React/App.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useRef, useMemo } from 'react'; -import DataGrid, { Column, Scrolling, SearchPanel, Toolbar, Item, DataGridRef, DataGridTypes } from 'devextreme-react/data-grid'; -import NumberBox, { NumberBoxTypes } from 'devextreme-react/number-box'; +import DataGrid, { Column, Scrolling, SearchPanel, Toolbar, Item, type DataGridTypes } from 'devextreme-react/data-grid'; +import NumberBox, { type NumberBoxTypes } from 'devextreme-react/number-box'; +import DataSource from 'devextreme/data/data_source'; import { createStore } from 'devextreme-aspnet-data-nojquery'; const url = 'https://js.devexpress.com/Demos/NetCore/api/DataGridSemanticSearch'; @@ -9,25 +10,24 @@ const App = () => { const searchValueRef = useRef(''); const similarityFactorRef = useRef(0.31); - const gridRef = useRef(null); - - const store = useMemo(() => createStore({ - key: 'ID', - loadUrl: `${url}/Get`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - ajaxOptions.data = { - ...ajaxOptions.data, - searchValue: searchValueRef.current, - similarityFactor: similarityFactorRef.current, - }; - }, + const dataSource = useMemo(() => new DataSource({ + store: createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + loadParams: { + searchValue: () => searchValueRef.current, + similarityFactor: () => similarityFactorRef.current, + }, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }) }), []); const onSimilarityFactorChanged = useCallback(({ value }: NumberBoxTypes.ValueChangedEvent) => { similarityFactorRef.current = value; if (searchValueRef.current !== '') { - gridRef.current?.instance().getDataSource().reload(); + dataSource.reload(); } }, []); @@ -35,9 +35,7 @@ const App = () => { if (e.parentType === 'searchPanel') { let searchTimeout: ReturnType | undefined; e.editorOptions.onValueChanged = (args: { value: string }) => { - if (searchTimeout) { - clearTimeout(searchTimeout); - } + clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchValueRef.current = args.value; e.component.getDataSource().reload(); @@ -49,8 +47,7 @@ const App = () => { return ( { const searchValueRef = useRef(''); const similarityFactorRef = useRef(0.31); - const gridRef = useRef(null); - const store = useMemo( + const dataSource = useMemo( () => - createStore({ - key: 'ID', - loadUrl: `${url}/Get`, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - ajaxOptions.data = { - ...ajaxOptions.data, - searchValue: searchValueRef.current, - similarityFactor: similarityFactorRef.current, - }; - }, + new DataSource({ + store: createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + loadParams: { + searchValue: () => searchValueRef.current, + similarityFactor: () => similarityFactorRef.current, + }, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }), }), [], ); const onSimilarityFactorChanged = useCallback(({ value }) => { similarityFactorRef.current = value; if (searchValueRef.current !== '') { - gridRef.current?.instance().getDataSource().reload(); + dataSource.reload(); } }, []); const onEditorPreparing = useCallback((e) => { if (e.parentType === 'searchPanel') { let searchTimeout; e.editorOptions.onValueChanged = (args) => { - if (searchTimeout) { - clearTimeout(searchTimeout); - } + clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchValueRef.current = args.value; e.component.getDataSource().reload(); @@ -52,8 +52,7 @@ const App = () => { }, []); return ( | null>(null); const searchValue = ref(''); const similarityFactor = ref(0.31); -const dataSource = createStore({ - key: 'ID', - loadUrl: `${url}/Get`, - loadParams: { - searchValue: () => searchValue.value, - similarityFactor: () => similarityFactor.value, - }, - onBeforeSend(method, ajaxOptions) { - ajaxOptions.xhrFields = { withCredentials: true }; - }, +const dataSource = new DataSource({ + store: createStore({ + key: 'ID', + loadUrl: `${url}/Get`, + loadParams: { + searchValue: () => searchValue.value, + similarityFactor: () => similarityFactor.value, + }, + onBeforeSend(method, ajaxOptions) { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }) }); const onSimilarityFactorChanged = (e: DxNumberBoxTypes.ValueChangedEvent) => { similarityFactor.value = e.value; if (searchValue.value !== '') { - dataGridRef.value!.instance!.getDataSource().reload(); + dataSource.reload(); } }; @@ -81,9 +81,7 @@ const onEditorPreparing = (e: DxDataGridTypes.EditorPreparingEvent) => { if (e.parentType === 'searchPanel') { let searchTimeout: ReturnType | undefined; e.editorOptions.onValueChanged = (args: { value: string }) => { - if (searchTimeout) { - clearTimeout(searchTimeout); - } + clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchValue.value = args.value; e.component.getDataSource().reload(); @@ -93,3 +91,8 @@ const onEditorPreparing = (e: DxDataGridTypes.EditorPreparingEvent) => { } }; + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html index 5aa7314c6053..5bfc9fb29a7c 100644 --- a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html +++ b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.html @@ -7,6 +7,7 @@ + diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js index 7c21c9651955..2dd62a5bcb18 100644 --- a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js +++ b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/index.js @@ -31,22 +31,25 @@ $(() => { { location: 'after', cssClass: 'align-bottom', - widget: 'dxNumberBox', - options: { - label: 'Similarity Factor', - labelMode: 'floating', - value: similarityFactor, - min: 0, - max: 1, - format: '0.00', - step: 0.05, - onValueChanged(e) { - similarityFactor = e.value; - if (searchValue !== '') { - dataGrid.getDataSource().reload(); - } - }, - }, + template: function (data, container) { + $('
') + .appendTo(container) + .dxNumberBox({ + label: 'Similarity Factor', + labelMode: 'floating', + value: similarityFactor, + min: 0, + max: 1, + format: '0.00', + step: 0.05, + onValueChanged(e) { + similarityFactor = e.value; + if (searchValue !== '') { + dataGrid.getDataSource().reload(); + } + }, + }); + } }, { name: 'searchPanel', diff --git a/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/styles.css b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/styles.css new file mode 100644 index 000000000000..49409f518a7b --- /dev/null +++ b/apps/demos/Demos/DataGrid/SemanticSearch/jQuery/styles.css @@ -0,0 +1,3 @@ +.align-bottom.dx-toolbar-item { + vertical-align: bottom; +} diff --git a/apps/demos/menuMeta.json b/apps/demos/menuMeta.json index bb9175e74ef8..a710a1798afd 100644 --- a/apps/demos/menuMeta.json +++ b/apps/demos/menuMeta.json @@ -38,6 +38,28 @@ "Modules": "openai" } ] + }, + { + "Name": "Semantic Search", + "Equivalents": "", + "Demos": [ + { + "Title": "Semantic Search", + "Name": "SemanticSearch", + "Widget": "DataGrid", + "Badge": "AI", + "DemoType": "Web", + "MvcAdditionalFiles": [ + "/DataProviders/SmartFilterProvider.cs", + "/Controllers/ApiControllers/DataGridSemanticSearchController.cs", + "/Services/EmbeddingsInitializerService.cs", + "/Models/DataGrid/DictionaryItem.cs", + "/Models/SampleData/DictionaryItems.cs" + ], + "TopLevel": true, + "Equivalents": "AI-Powered Search, Smart Search, Context-Aware Search, Natural Language Search, AI Search, Semantic Retrieval, Similarity Search" + } + ] } ] }, @@ -315,6 +337,21 @@ "/Models/SampleData/DataGridEmployees.cs" ], "DemoType": "Web" + }, + { + "Title": "Semantic Search", + "Name": "SemanticSearch", + "Widget": "DataGrid", + "Badge": "AI", + "DemoType": "Web", + "MvcAdditionalFiles": [ + "/DataProviders/SmartFilterProvider.cs", + "/Controllers/ApiControllers/DataGridSemanticSearchController.cs", + "/Services/EmbeddingsInitializerService.cs", + "/Models/DataGrid/DictionaryItem.cs", + "/Models/SampleData/DictionaryItems.cs" + ], + "Equivalents": "AI-Powered Search, Smart Search, Context-Aware Search, Natural Language Search, AI Search, Semantic Retrieval, Similarity Search" } ] }, @@ -784,6 +821,20 @@ } ] }, + { + "Name": "Semantic Search", + "Equivalents": "", + "Demos": [ + { + "Title": "Semantic Search", + "Name": "SemanticSearch", + "Widget": "DataGrid", + "Badge": "AI", + "DemoType": "Web", + "Equivalents": "AI-Powered Search, Smart Search, Context-Aware Search, Natural Language Search, AI Search, Semantic Retrieval, Similarity Search" + } + ] + }, { "Name": "Master-Detail", "Equivalents": "preview row", From f26d3d245539d95fdaef809890a402433ceb41d7 Mon Sep 17 00:00:00 2001 From: Andrei_Ser Date: Wed, 24 Jun 2026 19:55:33 +0400 Subject: [PATCH 3/3] update menumeta --- apps/demos/menuMeta.json | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/apps/demos/menuMeta.json b/apps/demos/menuMeta.json index a710a1798afd..ccc66e33ea50 100644 --- a/apps/demos/menuMeta.json +++ b/apps/demos/menuMeta.json @@ -49,6 +49,7 @@ "Widget": "DataGrid", "Badge": "AI", "DemoType": "Web", + "Modules": "devextreme-aspnet-data-nojquery", "MvcAdditionalFiles": [ "/DataProviders/SmartFilterProvider.cs", "/Controllers/ApiControllers/DataGridSemanticSearchController.cs", @@ -344,6 +345,7 @@ "Widget": "DataGrid", "Badge": "AI", "DemoType": "Web", + "Modules": "devextreme-aspnet-data-nojquery", "MvcAdditionalFiles": [ "/DataProviders/SmartFilterProvider.cs", "/Controllers/ApiControllers/DataGridSemanticSearchController.cs", @@ -821,20 +823,6 @@ } ] }, - { - "Name": "Semantic Search", - "Equivalents": "", - "Demos": [ - { - "Title": "Semantic Search", - "Name": "SemanticSearch", - "Widget": "DataGrid", - "Badge": "AI", - "DemoType": "Web", - "Equivalents": "AI-Powered Search, Smart Search, Context-Aware Search, Natural Language Search, AI Search, Semantic Retrieval, Similarity Search" - } - ] - }, { "Name": "Master-Detail", "Equivalents": "preview row",