@@ -28,24 +28,26 @@ import androidx.compose.runtime.saveable.rememberSaveable
28
28
import com.arcgismaps.data.Domain
29
29
import com.arcgismaps.data.FieldType
30
30
import com.arcgismaps.data.RangeDomain
31
+ import com.arcgismaps.exceptions.FeatureFormValidationException
31
32
import com.arcgismaps.mapping.featureforms.FeatureForm
32
33
import com.arcgismaps.mapping.featureforms.FieldFormElement
33
34
import com.arcgismaps.mapping.featureforms.TextAreaFormInput
34
35
import com.arcgismaps.mapping.featureforms.TextBoxFormInput
35
36
import com.arcgismaps.toolkit.featureforms.R
36
37
import com.arcgismaps.toolkit.featureforms.components.base.BaseFieldState
37
38
import com.arcgismaps.toolkit.featureforms.components.base.FieldProperties
39
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState
40
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.ExactCharConstraint
41
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.MaxCharConstraint
42
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.MaxNumericConstraint
43
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.MinMaxCharConstraint
44
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.MinMaxNumericConstraint
45
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.MinNumericConstraint
46
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.NoError
47
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.NotANumber
48
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.NotAWholeNumber
49
+ import com.arcgismaps.toolkit.featureforms.components.base.ValidationErrorState.Required
38
50
import com.arcgismaps.toolkit.featureforms.components.formelement.FieldElement
39
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.ExactCharConstraint
40
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.MaxCharConstraint
41
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.MaxNumericConstraint
42
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.MinMaxCharConstraint
43
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.MinMaxNumericConstraint
44
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.MinNumericConstraint
45
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.NoError
46
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.NotANumber
47
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.NotAWholeNumber
48
- import com.arcgismaps.toolkit.featureforms.components.text.ValidationErrorState.Required
49
51
import com.arcgismaps.toolkit.featureforms.utils.asDoubleTuple
50
52
import com.arcgismaps.toolkit.featureforms.utils.asLongTuple
51
53
import com.arcgismaps.toolkit.featureforms.utils.domain
@@ -95,12 +97,14 @@ internal class FormTextFieldState(
95
97
initialValue : String = properties.value.value,
96
98
scope : CoroutineScope ,
97
99
private val context : Context ,
98
- onEditValue : (Any? ) -> Unit
100
+ onEditValue : (Any? ) -> Unit ,
101
+ validate : () -> List <Throwable >
99
102
) : BaseFieldState<String>(
100
103
properties = properties,
101
104
initialValue = initialValue,
102
105
scope = scope,
103
- onEditValue = onEditValue
106
+ onEditValue = onEditValue,
107
+ validate = validate
104
108
) {
105
109
// indicates singleLine only if TextBoxFeatureFormInput
106
110
val singleLine = properties.singleLine
@@ -125,7 +129,6 @@ internal class FormTextFieldState(
125
129
private val _hasError = mutableStateOf(false )
126
130
private val _supportingTextIsErrorMessage = mutableStateOf(false )
127
131
val supportingTextIsErrorMessage: State <Boolean > = _supportingTextIsErrorMessage
128
-
129
132
/* *
130
133
* The domain of the element's field.
131
134
*/
@@ -236,38 +239,6 @@ internal class FormTextFieldState(
236
239
_hasError .value = errors.isNotEmpty()
237
240
}
238
241
239
- private fun validateTextRange (value : String ): ValidationErrorState =
240
- if (value.length !in minLength.. maxLength) {
241
- if (minLength > 0 && maxLength > 0 ) {
242
- if (minLength == maxLength) {
243
- ExactCharConstraint
244
- } else {
245
- MinMaxCharConstraint
246
- }
247
- } else {
248
- MaxCharConstraint
249
- }
250
- } else {
251
- NoError
252
- }
253
-
254
- private fun validateNumber (value : String ): ValidationErrorState =
255
- if (fieldType.isIntegerType) {
256
- val numberVal = value.toIntOrNull()
257
- if (numberVal == null ) {
258
- NotAWholeNumber
259
- } else {
260
- validateNumericRange(numberVal)
261
- }
262
- } else {
263
- val numberVal = value.toDoubleOrNull()
264
- if (numberVal == null ) {
265
- NotANumber
266
- } else {
267
- validateNumericRange(numberVal)
268
- }
269
- }
270
-
271
242
private fun validateNumericRange (numberVal : Int ): ValidationErrorState {
272
243
require(fieldType.isIntegerType)
273
244
return if (domain != null && domain is RangeDomain ) {
@@ -340,7 +311,6 @@ internal class FormTextFieldState(
340
311
validationErrors.firstOrNull { it != Required && it != NotANumber && it != NotAWholeNumber } ? : NoError
341
312
} else {
342
313
// if non empty, focused, show any error other than required (the Required error shouldn't be in the list)
343
- check (! validationErrors.contains(Required ))
344
314
validationErrors.first()
345
315
}
346
316
} else if (hasBeenFocused) {
@@ -354,32 +324,58 @@ internal class FormTextFieldState(
354
324
}
355
325
} else {
356
326
// if non empty, unfocused, show any error other than required (the Required error shouldn't be in the list)
357
- check (! validationErrors.contains(Required ))
358
327
validationErrors.first()
359
328
}
360
329
} else {
361
330
// never been focused
362
331
NoError
363
332
}
364
333
365
- private fun validate (value : String ): MutableList <ValidationErrorState > {
334
+ private fun validate (value : String ): List <ValidationErrorState > {
335
+ val errors = validate()
366
336
val ret = mutableListOf<ValidationErrorState >()
367
- if (isRequired.value && value.isEmpty() ) {
337
+ if (errors.any { it is FeatureFormValidationException . RequiredException } ) {
368
338
ret + = Required
369
339
}
370
-
340
+
371
341
if (! fieldType.isNumeric) {
372
- val rangeError = validateTextRange(value)
373
- if (rangeError != NoError ) {
374
- ret + = rangeError
342
+ if (errors.any { it is FeatureFormValidationException .MinCharConstraintException }
343
+ || errors.any { it is FeatureFormValidationException .MaxCharConstraintException }
344
+ ) {
345
+ if (minLength > 0 && maxLength > 0 ) {
346
+ if (minLength == maxLength) {
347
+ ret + = ExactCharConstraint
348
+ } else {
349
+ ret + = MinMaxCharConstraint
350
+ }
351
+ } else {
352
+ ret + = MaxCharConstraint
353
+ }
375
354
}
376
355
} else {
377
- val error = validateNumber(value)
378
- if (error != NoError ) {
379
- ret + = error
356
+ if (fieldType.isIntegerType) {
357
+ val numberVal = value.toIntOrNull()
358
+ if (numberVal == null ) {
359
+ ret + = NotAWholeNumber
360
+ } else {
361
+ val error = validateNumericRange(numberVal)
362
+ if (error != NoError ) {
363
+ ret + = error
364
+ }
365
+ }
366
+ } else {
367
+ val numberVal = value.toDoubleOrNull()
368
+ if (numberVal == null ) {
369
+ ret + = NotANumber
370
+ } else {
371
+ val error = validateNumericRange(numberVal)
372
+ if (error != NoError ) {
373
+ ret + = error
374
+ }
375
+ }
380
376
}
381
377
}
382
-
378
+
383
379
return ret
384
380
}
385
381
@@ -425,6 +421,7 @@ internal class FormTextFieldState(
425
421
form.editValue(formElement, newValue)
426
422
scope.launch { form.evaluateExpressions() }
427
423
},
424
+ validate = { formElement.getValidationErrors() }
428
425
).apply {
429
426
// focus is lost on rotation. https://devtopia.esri.com/runtime/apollo/issues/230
430
427
hasBeenFocused = list[1 ] as Boolean
@@ -459,30 +456,18 @@ internal fun rememberFormTextFieldState(
459
456
fieldType = form.fieldType(field),
460
457
domain = form.domain(field) as ? RangeDomain ,
461
458
minLength = minLength,
462
- maxLength = maxLength,
459
+ maxLength = maxLength
463
460
),
464
461
scope = scope,
465
462
context = context,
466
463
onEditValue = {
467
464
form.editValue(field, it)
468
465
scope.launch { form.evaluateExpressions() }
469
- }
466
+ },
467
+ validate = { field.getValidationErrors() }
470
468
)
471
469
}
472
470
473
- private sealed class ValidationErrorState {
474
- object NoError: ValidationErrorState()
475
- object Required: ValidationErrorState()
476
- object MinMaxCharConstraint: ValidationErrorState()
477
- object ExactCharConstraint: ValidationErrorState()
478
- object MaxCharConstraint: ValidationErrorState()
479
- object MinNumericConstraint: ValidationErrorState()
480
- object MaxNumericConstraint: ValidationErrorState()
481
- object MinMaxNumericConstraint: ValidationErrorState()
482
- object NotANumber: ValidationErrorState()
483
- object NotAWholeNumber: ValidationErrorState()
484
- }
485
-
486
471
/* *
487
472
* Provide a format string for any numeric type.
488
473
*
0 commit comments