@@ -45,8 +45,14 @@ import androidx.compose.ui.res.painterResource
4545import androidx.compose.ui.res.stringResource
4646import androidx.compose.ui.semantics.error
4747import androidx.compose.ui.semantics.semantics
48+ import androidx.compose.ui.text.TextRange
49+ import androidx.compose.ui.text.buildAnnotatedString
4850import androidx.compose.ui.text.input.ImeAction
4951import androidx.compose.ui.text.input.KeyboardType
52+ import androidx.compose.ui.text.input.OffsetMapping
53+ import androidx.compose.ui.text.input.TextFieldValue
54+ import androidx.compose.ui.text.input.TransformedText
55+ import androidx.compose.ui.text.input.VisualTransformation
5056import com.google.android.fhir.datacapture.R
5157import com.google.android.fhir.datacapture.extensions.format
5258import com.google.android.fhir.datacapture.extensions.toLocalDate
@@ -70,7 +76,15 @@ internal fun DatePickerItem(
7076 val focusManager = LocalFocusManager .current
7177 val keyboardController = LocalSoftwareKeyboardController .current
7278 var dateInputState by remember(dateInput) { mutableStateOf(dateInput) }
73- val dateInputDisplay by remember(dateInputState) { derivedStateOf { dateInputState.display } }
79+ val dateInputDisplay by
80+ remember(dateInputState) {
81+ derivedStateOf {
82+ TextFieldValue (
83+ text = dateInputState.display,
84+ selection = TextRange (dateInputFormat.patternWithDelimiters.length),
85+ )
86+ }
87+ }
7488
7589 var showDatePickerModal by remember { mutableStateOf(false ) }
7690
@@ -83,11 +97,12 @@ internal fun DatePickerItem(
8397 OutlinedTextField (
8498 value = dateInputDisplay,
8599 onValueChange = {
100+ val text = it.text
86101 if (
87- it .length <= dateInputFormat.patternWithoutDelimiters.length &&
88- it .all { char -> char.isDigit() }
102+ text .length <= dateInputFormat.patternWithoutDelimiters.length &&
103+ text .all { char -> char.isDigit() }
89104 ) {
90- val trimmedText = it .trim()
105+ val trimmedText = text .trim()
91106 val localDate =
92107 if (
93108 trimmedText.isNotBlank() &&
@@ -97,7 +112,7 @@ internal fun DatePickerItem(
97112 } else {
98113 null
99114 }
100- dateInputState = DateInput (it , localDate)
115+ dateInputState = DateInput (text , localDate)
101116 }
102117 },
103118 singleLine = true ,
@@ -132,7 +147,25 @@ internal fun DatePickerItem(
132147 KeyboardActions (
133148 onNext = { focusManager.moveFocus(FocusDirection .Down ) },
134149 ),
135- visualTransformation = DateVisualTransformation (dateInputFormat),
150+ visualTransformation =
151+ if (! dateInputFormat.delimiterExistsInPattern) {
152+ VisualTransformation .None
153+ } else {
154+ VisualTransformation { originalText ->
155+ val text = buildAnnotatedString {
156+ originalText.forEachIndexed { index, ch ->
157+ append(ch)
158+ if (
159+ index + 1 == dateInputFormat.delimiterFirstIndex ||
160+ index + 2 == dateInputFormat.delimiterLastIndex
161+ ) {
162+ append(dateInputFormat.delimiter)
163+ }
164+ }
165+ }
166+ TransformedText (text, dateInputFormat.offsetMapping)
167+ }
168+ },
136169 )
137170
138171 if (selectableDates != null && showDatePickerModal) {
@@ -197,4 +230,35 @@ typealias DateFormatPattern = String
197230
198231data class DateInput (val display : String , val value : LocalDate ? )
199232
233+ data class DateInputFormat (val patternWithDelimiters : String , val delimiter : Char ) {
234+ val patternWithoutDelimiters: String = patternWithDelimiters.replace(delimiter.toString(), " " )
235+
236+ val delimiterFirstIndex: Int = patternWithDelimiters.indexOf(delimiter)
237+ val delimiterLastIndex: Int = patternWithDelimiters.lastIndexOf(delimiter)
238+ val delimiterExistsInPattern = delimiterFirstIndex != - 1 && delimiterLastIndex != - 1
239+
240+ val offsetMapping =
241+ object : OffsetMapping {
242+ override fun originalToTransformed (offset : Int ): Int {
243+ return when {
244+ delimiterExistsInPattern &&
245+ offset >= delimiterLastIndex &&
246+ delimiterLastIndex > delimiterFirstIndex -> offset + 2
247+ delimiterExistsInPattern && offset >= delimiterFirstIndex -> offset + 1
248+ else -> offset
249+ }
250+ }
251+
252+ override fun transformedToOriginal (offset : Int ): Int {
253+ return when {
254+ delimiterExistsInPattern &&
255+ offset >= delimiterLastIndex &&
256+ offset > delimiterFirstIndex -> offset - 2
257+ delimiterExistsInPattern && offset >= delimiterFirstIndex -> offset - 1
258+ else -> offset
259+ }
260+ }
261+ }
262+ }
263+
200264const val DATE_TEXT_INPUT_FIELD = " date_picker_text_field"
0 commit comments