@@ -66,6 +66,42 @@ 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+ searchbar : false ,
74+ } ) ;
75+
76+ const getValidationClasses = ( fieldName : keyof typeof touched , value : string | number | null | undefined ) => {
77+ const isTouched = touched [ fieldName ] ;
78+ let isValid = false ;
79+
80+ // Handle ion-input-otp which has multiple inputs
81+ if ( fieldName === 'inputOtp' ) {
82+ // input-otp needs to check if all inputs are filled
83+ // (value length equals component length)
84+ const valueStr = String ( value || '' ) ;
85+ isValid = valueStr . length === 4 ;
86+ } else {
87+ const isEmpty = value === '' || value === null || value === undefined ;
88+ isValid = ! isEmpty ;
89+ }
90+
91+ // Always return validation classes
92+ // ion-touched is only added on blur
93+ const classes : string [ ] = [ ] ;
94+ if ( isTouched ) {
95+ classes . push ( 'ion-touched' ) ;
96+ }
97+ if ( isValid ) {
98+ classes . push ( 'ion-valid' ) ;
99+ } else {
100+ classes . push ( 'ion-invalid' ) ;
101+ }
102+ return classes . join ( ' ' ) ;
103+ } ;
104+
69105 const reset = ( ) => {
70106 setCheckbox ( false ) ;
71107 setToggle ( false ) ;
@@ -78,6 +114,12 @@ const Inputs: React.FC<InputsProps> = () => {
78114 setRadio ( 'red' ) ;
79115 setSegment ( 'dogs' ) ;
80116 setSelect ( 'apples' ) ;
117+ setTouched ( {
118+ input : false ,
119+ inputOtp : false ,
120+ textarea : false ,
121+ searchbar : false ,
122+ } ) ;
81123 } ;
82124
83125 const set = ( ) => {
@@ -122,6 +164,9 @@ const Inputs: React.FC<InputsProps> = () => {
122164 < IonSearchbar
123165 value = { searchbar }
124166 onIonInput = { ( e : IonSearchbarCustomEvent < SearchbarInputEventDetail > ) => setSearchbar ( e . detail . value ! ) }
167+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , searchbar : true } ) ) }
168+ className = { getValidationClasses ( 'searchbar' , searchbar ) }
169+ required
125170 > </ IonSearchbar >
126171 </ IonToolbar >
127172 </ IonHeader >
@@ -133,96 +178,107 @@ const Inputs: React.FC<InputsProps> = () => {
133178 </ IonToolbar >
134179 </ IonHeader >
135180
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 >
181+ < form >
182+ < IonItem >
183+ < IonCheckbox
184+ checked = { checkbox }
185+ onIonChange = { ( e : IonCheckboxCustomEvent < CheckboxChangeEventDetail > ) => setCheckbox ( e . detail . checked ) }
186+ >
187+ Checkbox
188+ </ IonCheckbox >
189+ </ IonItem >
190+
191+ < IonItem >
192+ < IonToggle
193+ checked = { toggle }
194+ onIonChange = { ( e : IonToggleCustomEvent < ToggleChangeEventDetail > ) => setToggle ( e . detail . checked ) }
195+ >
196+ Toggle
197+ </ IonToggle >
198+ </ IonItem >
199+
200+ < IonItem >
201+ < IonInput
202+ value = { input }
203+ onIonInput = { ( e : IonInputCustomEvent < InputInputEventDetail > ) => setInput ( e . detail . value ! ) }
204+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , input : true } ) ) }
205+ className = { getValidationClasses ( 'input' , input ) }
206+ label = "Input"
207+ required
208+ > </ IonInput >
209+ </ IonItem >
210+
211+ < IonItem >
212+ < IonInputOtp
213+ value = { inputOtp }
214+ onIonInput = { ( e : IonInputOtpCustomEvent < InputOtpInputEventDetail > ) => setInputOtp ( e . detail . value ?? '' ) }
215+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , inputOtp : true } ) ) }
216+ className = { getValidationClasses ( 'inputOtp' , inputOtp ) }
217+ required
218+ > </ IonInputOtp >
219+ </ IonItem >
200220
201- < IonRadioGroup
202- value = { radio }
203- onIonChange = { ( e : IonRadioGroupCustomEvent < RadioGroupChangeEventDetail > ) => setRadio ( e . detail . value ) }
204- >
205221 < IonItem >
206- < IonRadio value = "red" > Red</ IonRadio >
222+ < IonRange
223+ label = "Range"
224+ dualKnobs = { true }
225+ min = { 0 }
226+ max = { 100 }
227+ value = { range }
228+ onIonChange = { ( e : IonRangeCustomEvent < RangeChangeEventDetail > ) => setRange ( e . detail . value as { lower : number ; upper : number } ) }
229+ > </ IonRange >
207230 </ IonItem >
231+
208232 < IonItem >
209- < IonRadio value = "green" > Green</ IonRadio >
233+ < IonTextarea
234+ value = { textarea }
235+ onIonInput = { ( e : IonTextareaCustomEvent < TextareaInputEventDetail > ) => setTextarea ( e . detail . value ! ) }
236+ onIonBlur = { ( ) => setTouched ( prev => ( { ...prev , textarea : true } ) ) }
237+ className = { getValidationClasses ( 'textarea' , textarea ) }
238+ label = "Textarea"
239+ required
240+ > </ IonTextarea >
210241 </ IonItem >
242+
211243 < IonItem >
212- < IonRadio value = "blue" > Blue</ IonRadio >
244+ < IonLabel > Datetime</ IonLabel >
245+ < IonDatetime
246+ value = { datetime }
247+ onIonChange = { ( e : IonDatetimeCustomEvent < DatetimeChangeEventDetail > ) => {
248+ const value = e . detail . value ;
249+ if ( typeof value === 'string' ) {
250+ setDatetime ( value ) ;
251+ }
252+ } }
253+ > </ IonDatetime >
213254 </ IonItem >
214- </ IonRadioGroup >
215255
216- < IonItem >
217- < IonSelect
218- value = { select }
219- onIonChange = { ( e : IonSelectCustomEvent < SelectChangeEventDetail < any > > ) => setSelect ( e . detail . value ) }
220- label = "Select"
256+ < IonRadioGroup
257+ value = { radio }
258+ onIonChange = { ( e : IonRadioGroupCustomEvent < RadioGroupChangeEventDetail > ) => setRadio ( e . detail . value ) }
221259 >
222- < IonSelectOption value = "apples" > Apples</ IonSelectOption >
223- < IonSelectOption value = "bananas" > Bananas</ IonSelectOption >
224- </ IonSelect >
225- </ IonItem >
260+ < IonItem >
261+ < IonRadio value = "red" > Red</ IonRadio >
262+ </ IonItem >
263+ < IonItem >
264+ < IonRadio value = "green" > Green</ IonRadio >
265+ </ IonItem >
266+ < IonItem >
267+ < IonRadio value = "blue" > Blue</ IonRadio >
268+ </ IonItem >
269+ </ IonRadioGroup >
270+
271+ < IonItem >
272+ < IonSelect
273+ value = { select }
274+ onIonChange = { ( e : IonSelectCustomEvent < SelectChangeEventDetail < any > > ) => setSelect ( e . detail . value ) }
275+ label = "Select"
276+ >
277+ < IonSelectOption value = "apples" > Apples</ IonSelectOption >
278+ < IonSelectOption value = "bananas" > Bananas</ IonSelectOption >
279+ </ IonSelect >
280+ </ IonItem >
281+ </ form >
226282
227283 < div className = "ion-padding" >
228284 Checkbox: { checkbox . toString ( ) } < br />
0 commit comments