11import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
2+ import classNames from 'classnames' ;
23import { TableUtils } from '@deephaven/jsapi-utils' ;
34import type { dh as DhType } from '@deephaven/jsapi-types' ;
45import Log from '@deephaven/log' ;
@@ -140,6 +141,7 @@ function getNumberInputs(
140141 handleValueChange : ( e : React . ChangeEvent < HTMLInputElement > ) => void ,
141142 handleStartValueChange : ( e : React . ChangeEvent < HTMLInputElement > ) => void ,
142143 handleEndValueChange : ( e : React . ChangeEvent < HTMLInputElement > ) => void ,
144+ isInvalid : boolean ,
143145 conditionValue ?: string ,
144146 startValue ?: string ,
145147 endValue ?: string
@@ -154,7 +156,7 @@ function getNumberInputs(
154156 return (
155157 < input
156158 type = "number"
157- className = " form-control"
159+ className = { classNames ( ' form-control' , { 'is-invalid' : isInvalid } ) }
158160 placeholder = "Enter value"
159161 value = { conditionValue ?? '' }
160162 onChange = { handleValueChange }
@@ -165,14 +167,18 @@ function getNumberInputs(
165167 < div className = "d-flex flex-row" >
166168 < input
167169 type = "number"
168- className = "form-control d-flex mr-2"
170+ className = { classNames ( 'form-control' , 'd-flex' , 'mr-2' , {
171+ 'is-invalid' : isInvalid ,
172+ } ) }
169173 placeholder = "Start value"
170174 value = { startValue ?? '' }
171175 onChange = { handleStartValueChange }
172176 />
173177 < input
174178 type = "number"
175- className = "form-control d-flex"
179+ className = { classNames ( 'form-control' , 'd-flex' , {
180+ 'is-invalid' : isInvalid ,
181+ } ) }
176182 placeholder = "End value"
177183 value = { endValue ?? '' }
178184 onChange = { handleEndValueChange }
@@ -188,6 +194,7 @@ function getNumberInputs(
188194function getStringInputs (
189195 selectedCondition : StringCondition ,
190196 handleValueChange : ( e : React . ChangeEvent < HTMLInputElement > ) => void ,
197+ isInvalid : boolean ,
191198 conditionValue ?: string
192199) : JSX . Element | null {
193200 switch ( selectedCondition ) {
@@ -198,7 +205,7 @@ function getStringInputs(
198205 return (
199206 < input
200207 type = "text"
201- className = " form-control"
208+ className = { classNames ( ' form-control' , { 'is-invalid' : isInvalid } ) }
202209 placeholder = "Enter value"
203210 value = { conditionValue ?? '' }
204211 onChange = { handleValueChange }
@@ -210,6 +217,7 @@ function getStringInputs(
210217function getDateInputs (
211218 selectedCondition : DateCondition ,
212219 handleValueChange : ( e : React . ChangeEvent < HTMLInputElement > ) => void ,
220+ isInvalid : boolean ,
213221 conditionValue ?: string
214222) : JSX . Element | null {
215223 switch ( selectedCondition ) {
@@ -220,7 +228,7 @@ function getDateInputs(
220228 return (
221229 < input
222230 type = "text"
223- className = " form-control"
231+ className = { classNames ( ' form-control' , { 'is-invalid' : isInvalid } ) }
224232 placeholder = "Enter value"
225233 value = { conditionValue ?? '' }
226234 onChange = { handleValueChange }
@@ -236,6 +244,7 @@ function getBooleanInputs(): null {
236244function getCharInputs (
237245 selectedCondition : CharCondition ,
238246 handleValueChange : ( e : React . ChangeEvent < HTMLInputElement > ) => void ,
247+ isInvalid : boolean ,
239248 conditionValue ?: string
240249) : JSX . Element | null {
241250 switch ( selectedCondition ) {
@@ -246,7 +255,7 @@ function getCharInputs(
246255 return (
247256 < input
248257 type = "text"
249- className = " form-control"
258+ className = { classNames ( ' form-control' , { 'is-invalid' : isInvalid } ) }
250259 maxLength = { 1 }
251260 placeholder = "Enter value"
252261 value = { conditionValue ?? '' }
@@ -264,6 +273,7 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
264273 const [ conditionValue , setValue ] = useState ( config . value ) ;
265274 const [ startValue , setStartValue ] = useState ( config . start ) ;
266275 const [ endValue , setEndValue ] = useState ( config . end ) ;
276+ const [ isValid , setIsValid ] = useState ( true ) ;
267277
268278 if ( selectedColumnType !== prevColumnType ) {
269279 // Column type changed, reset condition and value fields
@@ -329,13 +339,13 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
329339
330340 useEffect (
331341 function changeCondition ( ) {
332- let isValid = true ;
342+ let isConditionValid = true ;
333343
334344 if ( selectedCondition === undefined ) {
335345 log . debug (
336346 'Unable to create formatting rule. Condition is not selected.'
337347 ) ;
338- isValid = false ;
348+ isConditionValid = false ;
339349 } else if (
340350 TableUtils . isNumberType ( column . type ) &&
341351 ! isNumberConditionValid (
@@ -349,7 +359,7 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
349359 'Unable to create formatting rule. Invalid value' ,
350360 conditionValue
351361 ) ;
352- isValid = false ;
362+ isConditionValid = false ;
353363 } else if (
354364 TableUtils . isDateType ( column . type ) &&
355365 ! isDateConditionValid (
@@ -362,17 +372,18 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
362372 'Unable to create formatting rule. Invalid date condition' ,
363373 conditionValue
364374 ) ;
365- isValid = false ;
375+ isConditionValid = false ;
366376 }
367377
378+ setIsValid ( isConditionValid ) ;
368379 onChange (
369380 {
370381 condition : selectedCondition ,
371382 value : conditionValue ,
372383 start : startValue ,
373384 end : endValue ,
374385 } ,
375- isValid
386+ isConditionValid
376387 ) ;
377388 } ,
378389 [
@@ -391,12 +402,26 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
391402 // Column not selected
392403 return null ;
393404 }
405+
406+ // Show invalid state only when there's a non-empty value that fails validation
407+ const hasInvalidValue =
408+ ! isValid && conditionValue !== undefined && conditionValue !== '' ;
409+
394410 if ( TableUtils . isNumberType ( selectedColumnType ) ) {
411+ // For IS_BETWEEN, show invalid on each field only if that field has a value
412+ const showInvalid =
413+ selectedCondition === NumberCondition . IS_BETWEEN
414+ ? ! isValid &&
415+ ( ( startValue !== undefined && startValue !== '' ) ||
416+ ( endValue !== undefined && endValue !== '' ) )
417+ : hasInvalidValue ;
418+
395419 return getNumberInputs (
396420 selectedCondition as NumberCondition ,
397421 handleValueChange ,
398422 handleStartValueChange ,
399423 handleEndValueChange ,
424+ showInvalid ,
400425 conditionValue ,
401426 startValue ,
402427 endValue
@@ -406,20 +431,23 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
406431 return getCharInputs (
407432 selectedCondition as CharCondition ,
408433 handleValueChange ,
434+ hasInvalidValue ,
409435 conditionValue
410436 ) ;
411437 }
412438 if ( TableUtils . isStringType ( selectedColumnType ) ) {
413439 return getStringInputs (
414440 selectedCondition as StringCondition ,
415441 handleValueChange ,
442+ hasInvalidValue ,
416443 conditionValue
417444 ) ;
418445 }
419446 if ( TableUtils . isDateType ( selectedColumnType ) ) {
420447 return getDateInputs (
421448 selectedCondition as DateCondition ,
422449 handleValueChange ,
450+ hasInvalidValue ,
423451 conditionValue
424452 ) ;
425453 }
@@ -432,6 +460,7 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element {
432460 conditionValue ,
433461 startValue ,
434462 endValue ,
463+ isValid ,
435464 handleValueChange ,
436465 handleStartValueChange ,
437466 handleEndValueChange ,
0 commit comments