diff --git a/samples/interactions/query-builder/overview/src/index.tsx b/samples/interactions/query-builder/overview/src/index.tsx index d1aed10fbb..1a2bb81481 100644 --- a/samples/interactions/query-builder/overview/src/index.tsx +++ b/samples/interactions/query-builder/overview/src/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { useRef, useEffect } from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import { @@ -24,143 +24,58 @@ interface Entity { fields: Field[]; } -interface SampleState { - expressionTree: IgrExpressionTree | null; -} - -export default class Sample extends React.Component { - private queryBuilderRef: React.RefObject; - private gridRef: React.RefObject; - - constructor(props: any) { - super(props); - - this.queryBuilderRef = React.createRef(); - this.gridRef = React.createRef(); - - this.state = { - expressionTree: null - }; - } - - componentDidMount() { - // Initialize expression tree - const tree = new IgrFilteringExpressionsTree(); - tree.operator = FilteringLogic.And; - tree.entity = 'Orders'; - tree.returnFields = [ - 'orderId', - 'customerId', - 'employeeId', - 'shipperId', - 'orderDate', - 'requiredDate', - 'shipVia', - 'freight', - 'shipName', - 'completed' - ]; - - this.setState({ expressionTree: tree }); - - // Set up query builder - if (this.queryBuilderRef.current && tree) { - const queryBuilder = this.queryBuilderRef.current; - queryBuilder.entities = this.entities as any; - queryBuilder.expressionTree = tree; - - queryBuilder.addEventListener('expressionTreeChange', this.handleExpressionTreeChange); - } - - // Set up grid - if (this.gridRef.current) { - const grid = this.gridRef.current; - grid.height = '420px'; - grid.autoGenerate = true; - } - } - - componentDidUpdate(prevProps: any, prevState: any) { - // Fetch data when expression tree changes - if (prevState.expressionTree !== this.state.expressionTree && this.state.expressionTree) { - this.fetchData(); - } - - // Update query builder if expression tree changed - if (this.queryBuilderRef.current && this.state.expressionTree && - prevState.expressionTree !== this.state.expressionTree) { - const queryBuilder = this.queryBuilderRef.current; - queryBuilder.expressionTree = this.state.expressionTree; - } - } - - componentWillUnmount() { - if (this.queryBuilderRef.current) { - this.queryBuilderRef.current.removeEventListener('expressionTreeChange', this.handleExpressionTreeChange); - } - } - - private handleExpressionTreeChange = (event: any) => { - this.setState({ expressionTree: event.detail }); - }; - - private get customersFields(): Field[] { - return [ - { field: 'customerId', dataType: 'string' }, - { field: 'companyName', dataType: 'string' }, - { field: 'contactName', dataType: 'string' }, - { field: 'contactTitle', dataType: 'string' } - ]; - } - - private get ordersFields(): Field[] { - return [ - { field: 'orderId', dataType: 'number' }, - { field: 'customerId', dataType: 'string' }, - { field: 'employeeId', dataType: 'number' }, - { field: 'shipperId', dataType: 'number' }, - { field: 'orderDate', dataType: 'date' }, - { field: 'requiredDate', dataType: 'date' }, - { field: 'shipVia', dataType: 'string' }, - { field: 'freight', dataType: 'number' }, - { field: 'shipName', dataType: 'string' }, - { field: 'completed', dataType: 'boolean' } - ]; - } - - private get entities(): Entity[] { - return [ - { name: 'Customers', fields: this.customersFields }, - { name: 'Orders', fields: this.ordersFields } - ]; - } - - private calculateColumnsInView = () => { - if (!this.gridRef.current || !this.state.expressionTree) return; - - const grid = this.gridRef.current; - const expressionTree = this.state.expressionTree; - const returnFields = expressionTree.returnFields ?? []; +const customersFields: Field[] = [ + { field: 'customerId', dataType: 'string' }, + { field: 'companyName', dataType: 'string' }, + { field: 'contactName', dataType: 'string' }, + { field: 'contactTitle', dataType: 'string' } +]; + +const ordersFields: Field[] = [ + { field: 'orderId', dataType: 'number' }, + { field: 'customerId', dataType: 'string' }, + { field: 'employeeId', dataType: 'number' }, + { field: 'shipperId', dataType: 'number' }, + { field: 'orderDate', dataType: 'date' }, + { field: 'requiredDate', dataType: 'date' }, + { field: 'shipVia', dataType: 'string' }, + { field: 'freight', dataType: 'number' }, + { field: 'shipName', dataType: 'string' }, + { field: 'completed', dataType: 'boolean' } +]; + +const entities: Entity[] = [ + { name: 'Customers', fields: customersFields }, + { name: 'Orders', fields: ordersFields } +]; + +function Sample() { + const queryBuilderRef = useRef(null); + const gridRef = useRef(null); + + const calculateColumnsInView = (tree: IgrExpressionTree) => { + if (!gridRef.current) return; + + const grid = gridRef.current; + const returnFields = tree.returnFields ?? []; if (returnFields.length === 0 || returnFields[0] === '*') { - const selectedEntity = this.entities.find(e => e.name === expressionTree.entity); + const selectedEntity = entities.find(e => e.name === tree.entity); const selectedEntityFields = (selectedEntity?.fields ?? []).map(f => f.field); - - grid.columns.forEach(column => { + + grid.columns.forEach((column: any) => { column.hidden = !selectedEntityFields.includes(column.field); }); } else { - grid.columns.forEach(column => { + grid.columns.forEach((column: any) => { column.hidden = !returnFields.includes(column.field); }); } }; - private async fetchData() { - const grid = this.gridRef.current; - const expressionTree = this.state.expressionTree; - - if (!grid || !expressionTree) return; + const fetchData = async (tree: IgrExpressionTree) => { + const grid = gridRef.current; + if (!grid) return; grid.isLoading = true; @@ -168,7 +83,7 @@ export default class Sample extends React.Component { const response = await fetch(`${API_ENDPOINT}/QueryBuilder/ExecuteQuery`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(expressionTree) + body: JSON.stringify(tree) }); if (!response.ok) { @@ -179,32 +94,73 @@ export default class Sample extends React.Component { const data = (Object.values(json)[0] as any[]) ?? []; grid.data = data; - // Calculate column visibility after data loads await new Promise(resolve => requestAnimationFrame(() => resolve(null))); - this.calculateColumnsInView(); + calculateColumnsInView(tree); } catch (err) { console.error(err); grid.data = []; } finally { grid.isLoading = false; } - } - - public render(): JSX.Element { - return ( -
-
- - -
- -
+ }; + + const handleExpressionTreeChange = (event: any) => { + fetchData(event.detail); + }; + + useEffect(() => { + const tree = new IgrFilteringExpressionsTree(); + tree.operator = FilteringLogic.And; + tree.entity = 'Orders'; + tree.returnFields = [ + 'orderId', + 'customerId', + 'employeeId', + 'shipperId', + 'orderDate', + 'requiredDate', + 'shipVia', + 'freight', + 'shipName', + 'completed' + ]; + + if (gridRef.current) { + gridRef.current.height = '420px'; + gridRef.current.autoGenerate = true; + } + + if (queryBuilderRef.current) { + const queryBuilder = queryBuilderRef.current; + queryBuilder.entities = entities as any; + queryBuilder.expressionTree = tree; + queryBuilder.addEventListener('expressionTreeChange', handleExpressionTreeChange); + } + + fetchData(tree); + + return () => { + if (queryBuilderRef.current) { + queryBuilderRef.current.removeEventListener('expressionTreeChange', handleExpressionTreeChange); + } + }; + }, []); + + return ( +
+
+ + +
+
- ); - } +
+ ); } +export default Sample; + // rendering above component in the React DOM const root = ReactDOM.createRoot(document.getElementById('root')); root.render(); diff --git a/samples/interactions/query-builder/template/src/index.tsx b/samples/interactions/query-builder/template/src/index.tsx index 9b5ad469b9..1625c18e71 100644 --- a/samples/interactions/query-builder/template/src/index.tsx +++ b/samples/interactions/query-builder/template/src/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { useRef, useEffect } from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; @@ -72,359 +72,326 @@ interface QueryBuilderSearchValueContext { defaultSearchValueTemplate?: any; } -interface SampleState { - expressionTree: IgrExpressionTree | null; -} +const regionOptions: RegionOption[] = [ + { text: 'Central North America', value: 'CNA' }, + { text: 'Central Europe', value: 'CEU' }, + { text: 'Mediterranean region', value: 'MED' }, + { text: 'Central Asia', value: 'CAS' }, + { text: 'South Asia', value: 'SAS' }, + { text: 'Western Africa', value: 'WAF' }, + { text: 'Amazonia', value: 'AMZ' }, + { text: 'Southern Africa', value: 'SAF' }, + { text: 'Northern Australia', value: 'NAU' } +]; -export default class Sample extends React.Component { - private queryBuilderRef: React.RefObject; - private expressionOutputRef: React.RefObject; - - private regionOptions: RegionOption[] = [ - { text: 'Central North America', value: 'CNA' }, - { text: 'Central Europe', value: 'CEU' }, - { text: 'Mediterranean region', value: 'MED' }, - { text: 'Central Asia', value: 'CAS' }, - { text: 'South Asia', value: 'SAS' }, - { text: 'Western Africa', value: 'WAF' }, - { text: 'Amazonia', value: 'AMZ' }, - { text: 'Southern Africa', value: 'SAF' }, - { text: 'Northern Australia', value: 'NAU' } - ]; - - private statusOptions: StatusOption[] = [ - { text: 'New', value: 1 }, - { text: 'Shipped', value: 2 }, - { text: 'Done', value: 3 } - ]; - - constructor(props: any) { - super(props); - - this.queryBuilderRef = React.createRef(); - this.expressionOutputRef = React.createRef(); - - this.state = { - expressionTree: null - }; +const statusOptions: StatusOption[] = [ + { text: 'New', value: 1 }, + { text: 'Shipped', value: 2 }, + { text: 'Done', value: 3 } +]; + +const ordersFields: Field[] = [ + { field: 'CompanyID', dataType: 'string' }, + { field: 'OrderID', dataType: 'number' }, + { field: 'Freight', dataType: 'number' }, + { field: 'ShipCountry', dataType: 'string' }, + { field: 'IsRushOrder', dataType: 'boolean' }, + { + field: 'RequiredTime', + dataType: 'time', + formatter: (value: any) => { + if (!value || !(value instanceof Date)) return ''; + return value.toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit' + }); + } + }, + { + field: 'OrderDate', + dataType: 'date', + formatter: (value: any) => { + if (!value || !(value instanceof Date)) return ''; + return value.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + }); + } + }, + { + field: 'Region', + dataType: 'string', + formatter: (value: any) => value?.text ?? value?.value ?? value + }, + { + field: 'OrderStatus', + dataType: 'number', + formatter: (value: number) => statusOptions.find(option => option.value === value)?.text ?? value } +]; - componentDidMount() { - // Initialize expression tree - const tree = new IgrFilteringExpressionsTree(); - tree.operator = FilteringLogic.And; - tree.entity = 'Orders'; - tree.returnFields = ['*']; - tree.filteringOperands.push({ - fieldName: 'Region', - condition: IgrStringFilteringOperand.instance().condition('equals'), - conditionName: 'equals', - searchVal: this.regionOptions[0] - } as any); - tree.filteringOperands.push({ - fieldName: 'OrderStatus', - condition: IgrStringFilteringOperand.instance().condition('equals'), - conditionName: 'equals', - searchVal: this.statusOptions[0].value - } as any); +const entities: Entity[] = [ + { + name: 'Orders', + fields: ordersFields + } +]; - this.setState({ expressionTree: tree }); +const setImplicitValue = (ctx: QueryBuilderSearchValueContext, value: any) => { + queueMicrotask(() => { + ctx.implicit.value = value; + }); +}; - // Set up query builder - if (this.queryBuilderRef.current && tree) { - const queryBuilder = this.queryBuilderRef.current; - queryBuilder.entities = this.entities as any; - queryBuilder.expressionTree = tree; +const normalizeTimeValue = (value: unknown): Date | null => { + if (!value) return null; + if (value instanceof Date) return value; - queryBuilder.addEventListener('expressionTreeChange', this.handleExpressionTreeChange); - } + if (typeof value === 'string') { + const isoCandidate = value.includes('T') ? value : `1970-01-01T${value}`; + const parsed = new Date(isoCandidate); + return isNaN(parsed.getTime()) ? null : parsed; } - componentDidUpdate(prevProps: any, prevState: any) { - // Update query builder if expression tree changed - if (this.queryBuilderRef.current && this.state.expressionTree && - prevState.expressionTree !== this.state.expressionTree) { - const queryBuilder = this.queryBuilderRef.current; - queryBuilder.expressionTree = this.state.expressionTree; - } - - // Render expression tree output - if (this.expressionOutputRef.current && this.state.expressionTree && - prevState.expressionTree !== this.state.expressionTree) { - this.expressionOutputRef.current.textContent = JSON.stringify(this.state.expressionTree, null, 2); - } + if (typeof value === 'number') { + const parsed = new Date(value); + return isNaN(parsed.getTime()) ? null : parsed; } - componentWillUnmount() { - if (this.queryBuilderRef.current) { - this.queryBuilderRef.current.removeEventListener('expressionTreeChange', this.handleExpressionTreeChange); - } + return null; +}; + +const buildRegionSelect = (ctx: QueryBuilderSearchValueContext) => { + const currentValue = ctx?.implicit?.value?.value ?? ''; + const key = `region-select-${currentValue}`; + + return ( + { + const value = sender.value; + const currentKey = ctx?.implicit?.value?.value ?? ''; + + if (!value || value === currentKey) return; + + setImplicitValue(ctx, regionOptions.find(option => option.value === value) ?? null); + }}> + {regionOptions.map(option => ( + + {option.text} + + ))} + + ); +}; + +const buildStatusRadios = (ctx: QueryBuilderSearchValueContext) => { + const implicitValue = ctx.implicit?.value; + const currentValue = implicitValue == null ? '' : implicitValue.toString(); + + return ( + + {statusOptions.map(option => ( + { + if (!e.detail.checked) return; + const numericValue = Number(e.detail.value); + if (ctx.implicit.value === numericValue) return; + setImplicitValue(ctx, numericValue); + }}> + {option.text} + + ))} + + ); +}; + +const buildDatePicker = (ctx: QueryBuilderSearchValueContext) => { + const implicitValue = ctx.implicit?.value; + const currentValue = implicitValue instanceof Date + ? implicitValue + : implicitValue + ? new Date(implicitValue) + : null; + + const allowedConditions = ['equals', 'doesNotEqual', 'before', 'after']; + const isEnabled = allowedConditions.indexOf(ctx.selectedCondition ?? '') !== -1; + const key = `date-picker-${currentValue}`; + + return ( + sender.show()} + change={(sender: any) => { + setImplicitValue(ctx, sender.value); + }}> + + ); +}; + +const buildTimeInput = (ctx: QueryBuilderSearchValueContext) => { + const currentValue = normalizeTimeValue(ctx.implicit?.value); + const allowedConditions = ['at', 'not_at', 'at_before', 'at_after', 'before', 'after']; + const isDisabled = ctx.selectedField == null || allowedConditions.indexOf(ctx.selectedCondition ?? '') === -1; + const key = `time-input-${currentValue}`; + + return ( + { + setImplicitValue(ctx, sender.value); + }}> +
+ +
+
+ ); +}; + +const buildDefaultInput = (ctx: QueryBuilderSearchValueContext, matchesEqualityCondition: boolean) => { + const selectedField = ctx.selectedField; + const dataType = selectedField?.dataType; + const isNumber = dataType === 'number'; + const isBoolean = dataType === 'boolean'; + + const placeholder = ctx.selectedCondition === 'inQuery' || ctx.selectedCondition === 'notInQuery' + ? 'Sub-query results' + : 'Value'; + + const currentValue = typeof ctx.implicit?.value === 'object' && (ctx.implicit.value && 'text' in ctx.implicit.value) + ? matchesEqualityCondition ? ctx.implicit.value.text : '' + : ctx.implicit?.value; + + const inputValue = currentValue == null ? '' : currentValue; + const disabledConditions = ['empty', 'notEmpty', 'null', 'notNull', 'inQuery', 'notInQuery']; + const isDisabled = isBoolean || selectedField == null || disabledConditions.indexOf(ctx.selectedCondition ?? '') !== -1; + const key = `default-input-${inputValue}`; + + return ( + { + const value = sender.value; + setImplicitValue(ctx, isNumber + ? value === '' ? null : Number(value) + : value); + }}> + + ); +}; + +const buildSearchValueTemplate = (ctx: QueryBuilderSearchValueContext) => { + const field = ctx.selectedField?.field; + const condition = ctx.selectedCondition; + const matchesEqualityCondition = condition === 'equals' || condition === 'doesNotEqual'; + + if (!ctx.implicit) { + ctx.implicit = { value: null }; } - private handleExpressionTreeChange = (event: any) => { - this.setState({ expressionTree: event.detail }); - }; - - private get ordersFields(): Field[] { - return [ - { field: 'CompanyID', dataType: 'string' }, - { field: 'OrderID', dataType: 'number' }, - { field: 'Freight', dataType: 'number' }, - { field: 'ShipCountry', dataType: 'string' }, - { field: 'IsRushOrder', dataType: 'boolean' }, - { - field: 'RequiredTime', - dataType: 'time', - formatter: (value: any) => { - if (!value || !(value instanceof Date)) return ''; - return value.toLocaleTimeString('en-US', { - hour: '2-digit', - minute: '2-digit' - }); - } - }, - { - field: 'OrderDate', - dataType: 'date', - formatter: (value: any) => { - if (!value || !(value instanceof Date)) return ''; - return value.toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric' - }); - } - }, - { - field: 'Region', - dataType: 'string', - formatter: (value: any) => value?.text ?? value?.value ?? value - }, - { - field: 'OrderStatus', - dataType: 'number', - formatter: (value: number) => this.statusOptions.find(option => option.value === value)?.text ?? value - } - ]; + if (field === 'Region' && matchesEqualityCondition) { + return buildRegionSelect(ctx); } - private get entities(): Entity[] { - return [ - { - name: 'Orders', - fields: this.ordersFields - } - ]; + if (field === 'OrderStatus' && matchesEqualityCondition) { + return buildStatusRadios(ctx); } - private setImplicitValue(ctx: QueryBuilderSearchValueContext, value: any) { - queueMicrotask(() => { - ctx.implicit.value = value; - }); + if (ctx.selectedField?.dataType === 'date') { + return buildDatePicker(ctx); } - private normalizeTimeValue = (value: unknown): Date | null => { - if (!value) return null; - if (value instanceof Date) return value; - - if (typeof value === 'string') { - const isoCandidate = value.includes('T') ? value : `1970-01-01T${value}`; - const parsed = new Date(isoCandidate); - return isNaN(parsed.getTime()) ? null : parsed; - } - - if (typeof value === 'number') { - const parsed = new Date(value); - return isNaN(parsed.getTime()) ? null : parsed; - } - - return null; - }; + if (ctx.selectedField?.dataType === 'time') { + return buildTimeInput(ctx); + } - private buildSearchValueTemplate = (ctx: QueryBuilderSearchValueContext) => { - const field = ctx.selectedField?.field; - const condition = ctx.selectedCondition; - const matchesEqualityCondition = condition === 'equals' || condition === 'doesNotEqual'; + return buildDefaultInput(ctx, matchesEqualityCondition); +}; - if (!ctx.implicit) { - ctx.implicit = { value: null }; - } +function Sample() { + const queryBuilderRef = useRef(null); + const expressionOutputRef = useRef(null); - if (field === 'Region' && matchesEqualityCondition) { - return this.buildRegionSelect(ctx); + const handleExpressionTreeChange = (event: any) => { + if (expressionOutputRef.current) { + expressionOutputRef.current.textContent = JSON.stringify(event.detail, null, 2); } + }; - if (field === 'OrderStatus' && matchesEqualityCondition) { - return this.buildStatusRadios(ctx); - } + useEffect(() => { + const tree = new IgrFilteringExpressionsTree(); + tree.operator = FilteringLogic.And; + tree.entity = 'Orders'; + tree.returnFields = ['*']; + tree.filteringOperands.push({ + fieldName: 'Region', + condition: IgrStringFilteringOperand.instance().condition('equals'), + conditionName: 'equals', + searchVal: regionOptions[0] + } as any); + tree.filteringOperands.push({ + fieldName: 'OrderStatus', + condition: IgrStringFilteringOperand.instance().condition('equals'), + conditionName: 'equals', + searchVal: statusOptions[0].value + } as any); - if (ctx.selectedField?.dataType === 'date') { - return this.buildDatePicker(ctx); + if (queryBuilderRef.current) { + const queryBuilder = queryBuilderRef.current; + queryBuilder.entities = entities as any; + queryBuilder.expressionTree = tree; + queryBuilder.addEventListener('expressionTreeChange', handleExpressionTreeChange); } - if (ctx.selectedField?.dataType === 'time') { - return this.buildTimeInput(ctx); + if (expressionOutputRef.current) { + expressionOutputRef.current.textContent = JSON.stringify(tree, null, 2); } - return this.buildDefaultInput(ctx, matchesEqualityCondition); - }; - - private buildRegionSelect = (ctx: QueryBuilderSearchValueContext) => { - const currentValue = ctx?.implicit?.value?.value ?? ''; - const key = `region-select-${currentValue}`; - - return ( - { - const value = sender.value; - const currentKey = ctx?.implicit?.value?.value ?? ''; - - if (!value || value === currentKey) return; - - this.setImplicitValue(ctx, this.regionOptions.find(option => option.value === value) ?? null); - }}> - {this.regionOptions.map(option => ( - - {option.text} - - ))} - - ); - }; - - private buildStatusRadios = (ctx: QueryBuilderSearchValueContext) => { - const implicitValue = ctx.implicit?.value; - const currentValue = implicitValue == null ? '' : implicitValue.toString(); - - return ( - - {this.statusOptions.map(option => ( - { - if (!e.detail.checked) return; - const numericValue = Number(e.detail.value); - if (ctx.implicit.value === numericValue) return; - this.setImplicitValue(ctx, numericValue); - }}> - {option.text} - - ))} - - ); - }; - - private buildDatePicker = (ctx: QueryBuilderSearchValueContext) => { - const implicitValue = ctx.implicit?.value; - const currentValue = implicitValue instanceof Date - ? implicitValue - : implicitValue - ? new Date(implicitValue) - : null; - - const allowedConditions = ['equals', 'doesNotEqual', 'before', 'after']; - const isEnabled = allowedConditions.indexOf(ctx.selectedCondition ?? '') !== -1; - const key = `date-picker-${currentValue}`; - - return ( - sender.show()} - change={(sender: any) => { - this.setImplicitValue(ctx, sender.value); - }}> - - ); - }; - - private buildTimeInput = (ctx: QueryBuilderSearchValueContext) => { - const currentValue = this.normalizeTimeValue(ctx.implicit?.value); - const allowedConditions = ['at', 'not_at', 'at_before', 'at_after', 'before', 'after']; - const isDisabled = ctx.selectedField == null || allowedConditions.indexOf(ctx.selectedCondition ?? '') === -1; - const key = `time-input-${currentValue}`; - - return ( - { - this.setImplicitValue(ctx, sender.value); - }}> -
- -
-
- ); - }; - - private buildDefaultInput = (ctx: QueryBuilderSearchValueContext, matchesEqualityCondition: boolean) => { - const selectedField = ctx.selectedField; - const dataType = selectedField?.dataType; - const isNumber = dataType === 'number'; - const isBoolean = dataType === 'boolean'; - - const placeholder = ctx.selectedCondition === 'inQuery' || ctx.selectedCondition === 'notInQuery' - ? 'Sub-query results' - : 'Value'; - - const currentValue = typeof ctx.implicit?.value === 'object' && (ctx.implicit.value && 'text' in ctx.implicit.value) - ? matchesEqualityCondition ? ctx.implicit.value.text : '' - : ctx.implicit?.value; - - const inputValue = currentValue == null ? '' : currentValue; - const disabledConditions = ['empty', 'notEmpty', 'null', 'notNull', 'inQuery', 'notInQuery']; - const isDisabled = isBoolean || selectedField == null || disabledConditions.indexOf(ctx.selectedCondition ?? '') !== -1; - const key = `default-input-${inputValue}`; - - return ( - { - const value = sender.value; - this.setImplicitValue(ctx, isNumber - ? value === '' ? null : Number(value) - : value); - }}> - - ); - }; - - public render(): JSX.Element { - return ( -
-
- - - - -
-

-          
+ return () => { + if (queryBuilderRef.current) { + queryBuilderRef.current.removeEventListener('expressionTreeChange', handleExpressionTreeChange); + } + }; + }, []); + + return ( +
+
+ + + + +
+

         
- ); - } +
+ ); } +export default Sample; + // rendering above component in the React DOM const root = ReactDOM.createRoot(document.getElementById('root')); root.render();