@@ -66,6 +66,41 @@ const Inputs: React.FC<InputsProps> = () => {
6666 const [ segment , setSegment ] = useState ( 'dogs' ) ;
6767 const [ select , setSelect ] = useState ( 'apples' ) ;
6868
69+ const [ touched , setTouched ] = useState ( {
70+ input : false ,
71+ inputOtp : false ,
72+ textarea : false ,
73+ } ) ;
74+
75+ const getValidationClasses = ( fieldName : keyof typeof touched , value : string | number | null | undefined ) => {
76+ const isTouched = touched [ fieldName ] ;
77+ let isValid = false ;
78+
79+ // Handle ion-input-otp which has multiple inputs
80+ if ( fieldName === 'inputOtp' ) {
81+ // input-otp needs to check if all inputs are filled
82+ // (value length equals component length)
83+ const valueStr = String ( value || '' ) ;
84+ isValid = valueStr . length === 4 ;
85+ } else {
86+ const isEmpty = value === '' || value === null || value === undefined ;
87+ isValid = ! isEmpty ;
88+ }
89+
90+ // Always return validation classes
91+ // ion-touched is only added on blur
92+ const classes : string [ ] = [ ] ;
93+ if ( isTouched ) {
94+ classes . push ( 'ion-touched' ) ;
95+ }
96+ if ( isValid ) {
97+ classes . push ( 'ion-valid' ) ;
98+ } else {
99+ classes . push ( 'ion-invalid' ) ;
100+ }
101+ return classes . join ( ' ' ) ;
102+ } ;
103+
69104 const reset = ( ) => {
70105 setCheckbox ( false ) ;
71106 setToggle ( false ) ;
@@ -78,6 +113,11 @@ const Inputs: React.FC<InputsProps> = () => {
78113 setRadio ( 'red' ) ;
79114 setSegment ( 'dogs' ) ;
80115 setSelect ( 'apples' ) ;
116+ setTouched ( {
117+ input : false ,
118+ inputOtp : false ,
119+ textarea : false ,
120+ } ) ;
81121 } ;
82122
83123 const set = ( ) => {
@@ -133,96 +173,107 @@ const Inputs: React.FC<InputsProps> = () => {
133173 </ IonToolbar >
134174 </ IonHeader >
135175
136- < IonItem >
137- < IonCheckbox
138- checked = { checkbox }
139- onIonChange = { ( e : IonCheckboxCustomEvent < CheckboxChangeEventDetail > ) => setCheckbox ( e . detail . checked ) }
140- >
141- Checkbox
142- </ IonCheckbox >
143- </ IonItem >
144-
145- < IonItem >
146- < IonToggle
147- checked = { toggle }
148- onIonChange = { ( e : IonToggleCustomEvent < ToggleChangeEventDetail > ) => setToggle ( e . detail . checked ) }
149- >
150- Toggle
151- </ IonToggle >
152- </ IonItem >
153-
154- < IonItem >
155- < IonInput
156- value = { input }
157- onIonInput = { ( e : IonInputCustomEvent < InputInputEventDetail > ) => setInput ( e . detail . value ! ) }
158- label = "Input"
159- > </ IonInput >
160- </ IonItem >
161-
162- < IonItem >
163- < IonInputOtp
164- value = { inputOtp }
165- onIonInput = { ( e : IonInputOtpCustomEvent < InputOtpInputEventDetail > ) => setInputOtp ( e . detail . value ?? '' ) }
166- > </ IonInputOtp >
167- </ IonItem >
168-
169- < IonItem >
170- < IonRange
171- label = "Range"
172- dualKnobs = { true }
173- min = { 0 }
174- max = { 100 }
175- value = { range }
176- onIonChange = { ( e : IonRangeCustomEvent < RangeChangeEventDetail > ) => setRange ( e . detail . value as { lower : number ; upper : number } ) }
177- > </ IonRange >
178- </ IonItem >
179-
180- < IonItem >
181- < IonTextarea
182- value = { textarea }
183- onIonInput = { ( e : IonTextareaCustomEvent < TextareaInputEventDetail > ) => setTextarea ( e . detail . value ! ) }
184- label = "Textarea"
185- > </ IonTextarea >
186- </ IonItem >
187-
188- < IonItem >
189- < IonLabel > Datetime</ IonLabel >
190- < IonDatetime
191- value = { datetime }
192- onIonChange = { ( e : IonDatetimeCustomEvent < DatetimeChangeEventDetail > ) => {
193- const value = e . detail . value ;
194- if ( typeof value === 'string' ) {
195- setDatetime ( value ) ;
196- }
197- } }
198- > </ IonDatetime >
199- </ IonItem >
176+ < form >
177+ < IonItem >
178+ < IonCheckbox
179+ checked = { checkbox }
180+ onIonChange = { ( e : IonCheckboxCustomEvent < CheckboxChangeEventDetail > ) => setCheckbox ( e . detail . checked ) }
181+ >
182+ Checkbox
183+ </ IonCheckbox >
184+ </ IonItem >
185+
186+ < IonItem >
187+ < IonToggle
188+ checked = { toggle }
189+ onIonChange = { ( e : IonToggleCustomEvent < ToggleChangeEventDetail > ) => setToggle ( e . detail . checked ) }
190+ >
191+ Toggle
192+ </ IonToggle >
193+ </ IonItem >
194+
195+ < IonItem >
196+ < IonInput
197+ value = { input }
198+ onIonInput = { ( e : IonInputCustomEvent < InputInputEventDetail > ) => setInput ( e . detail . value ! ) }
199+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , input : true } ) ) }
200+ className = { getValidationClasses ( 'input' , input ) }
201+ label = "Input"
202+ required
203+ > </ IonInput >
204+ </ IonItem >
205+
206+ < IonItem >
207+ < IonInputOtp
208+ value = { inputOtp }
209+ onIonInput = { ( e : IonInputOtpCustomEvent < InputOtpInputEventDetail > ) => setInputOtp ( e . detail . value ?? '' ) }
210+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , inputOtp : true } ) ) }
211+ className = { getValidationClasses ( 'inputOtp' , inputOtp ) }
212+ required
213+ > </ IonInputOtp >
214+ </ IonItem >
200215
201- < IonRadioGroup
202- value = { radio }
203- onIonChange = { ( e : IonRadioGroupCustomEvent < RadioGroupChangeEventDetail > ) => setRadio ( e . detail . value ) }
204- >
205216 < IonItem >
206- < IonRadio value = "red" > Red</ IonRadio >
217+ < IonRange
218+ label = "Range"
219+ dualKnobs = { true }
220+ min = { 0 }
221+ max = { 100 }
222+ value = { range }
223+ onIonChange = { ( e : IonRangeCustomEvent < RangeChangeEventDetail > ) => setRange ( e . detail . value as { lower : number ; upper : number } ) }
224+ > </ IonRange >
207225 </ IonItem >
226+
208227 < IonItem >
209- < IonRadio value = "green" > Green</ IonRadio >
228+ < IonTextarea
229+ value = { textarea }
230+ onIonInput = { ( e : IonTextareaCustomEvent < TextareaInputEventDetail > ) => setTextarea ( e . detail . value ! ) }
231+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , textarea : true } ) ) }
232+ className = { getValidationClasses ( 'textarea' , textarea ) }
233+ label = "Textarea"
234+ required
235+ > </ IonTextarea >
210236 </ IonItem >
237+
211238 < IonItem >
212- < IonRadio value = "blue" > Blue</ IonRadio >
239+ < IonLabel > Datetime</ IonLabel >
240+ < IonDatetime
241+ value = { datetime }
242+ onIonChange = { ( e : IonDatetimeCustomEvent < DatetimeChangeEventDetail > ) => {
243+ const value = e . detail . value ;
244+ if ( typeof value === 'string' ) {
245+ setDatetime ( value ) ;
246+ }
247+ } }
248+ > </ IonDatetime >
213249 </ IonItem >
214- </ IonRadioGroup >
215250
216- < IonItem >
217- < IonSelect
218- value = { select }
219- onIonChange = { ( e : IonSelectCustomEvent < SelectChangeEventDetail < any > > ) => setSelect ( e . detail . value ) }
220- label = "Select"
251+ < IonRadioGroup
252+ value = { radio }
253+ onIonChange = { ( e : IonRadioGroupCustomEvent < RadioGroupChangeEventDetail > ) => setRadio ( e . detail . value ) }
221254 >
222- < IonSelectOption value = "apples" > Apples</ IonSelectOption >
223- < IonSelectOption value = "bananas" > Bananas</ IonSelectOption >
224- </ IonSelect >
225- </ IonItem >
255+ < IonItem >
256+ < IonRadio value = "red" > Red</ IonRadio >
257+ </ IonItem >
258+ < IonItem >
259+ < IonRadio value = "green" > Green</ IonRadio >
260+ </ IonItem >
261+ < IonItem >
262+ < IonRadio value = "blue" > Blue</ IonRadio >
263+ </ IonItem >
264+ </ IonRadioGroup >
265+
266+ < IonItem >
267+ < IonSelect
268+ value = { select }
269+ onIonChange = { ( e : IonSelectCustomEvent < SelectChangeEventDetail < any > > ) => setSelect ( e . detail . value ) }
270+ label = "Select"
271+ >
272+ < IonSelectOption value = "apples" > Apples</ IonSelectOption >
273+ < IonSelectOption value = "bananas" > Bananas</ IonSelectOption >
274+ </ IonSelect >
275+ </ IonItem >
276+ </ form >
226277
227278 < div className = "ion-padding" >
228279 Checkbox: { checkbox . toString ( ) } < br />
0 commit comments