1616
1717package com.google.android.fhir.datacapture.views.compose
1818
19+ import androidx.compose.foundation.interaction.MutableInteractionSource
1920import androidx.compose.foundation.layout.Arrangement
21+ import androidx.compose.foundation.layout.Box
2022import androidx.compose.foundation.layout.FlowRow
23+ import androidx.compose.foundation.layout.PaddingValues
2124import androidx.compose.foundation.layout.fillMaxWidth
2225import androidx.compose.foundation.layout.padding
26+ import androidx.compose.foundation.text.BasicTextField
2327import androidx.compose.material3.ExperimentalMaterial3Api
2428import androidx.compose.material3.ExposedDropdownMenuAnchorType
2529import androidx.compose.material3.ExposedDropdownMenuBox
2630import androidx.compose.material3.Icon
2731import androidx.compose.material3.InputChip
28- import androidx.compose.material3.OutlinedTextField
32+ import androidx.compose.material3.OutlinedTextFieldDefaults
2933import androidx.compose.material3.Text
3034import androidx.compose.runtime.Composable
3135import androidx.compose.runtime.getValue
36+ import androidx.compose.runtime.mutableIntStateOf
3237import androidx.compose.runtime.mutableStateOf
3338import androidx.compose.runtime.remember
3439import androidx.compose.runtime.setValue
3540import androidx.compose.ui.Modifier
41+ import androidx.compose.ui.graphics.SolidColor
42+ import androidx.compose.ui.layout.onSizeChanged
43+ import androidx.compose.ui.platform.LocalDensity
3644import androidx.compose.ui.platform.testTag
3745import androidx.compose.ui.res.dimensionResource
3846import androidx.compose.ui.res.painterResource
3947import androidx.compose.ui.semantics.error
4048import androidx.compose.ui.semantics.semantics
4149import androidx.compose.ui.text.AnnotatedString
50+ import androidx.compose.ui.text.TextStyle
4251import androidx.compose.ui.text.input.TextFieldValue
52+ import androidx.compose.ui.text.input.VisualTransformation
4353import androidx.compose.ui.unit.dp
4454import com.google.android.fhir.datacapture.R
4555import com.google.android.fhir.datacapture.views.factories.DropDownAnswerOption
@@ -64,59 +74,116 @@ internal fun MultiAutoCompleteTextItem(
6474 options.filter { it.answerOptionString.contains(autoCompleteText.text, true ) }
6575 }
6676
77+ // Track the height of the chip container to add padding to text field
78+ var chipContainerHeight by remember { mutableIntStateOf(0 ) }
79+ val density = LocalDensity .current
80+ val chipMargin = dimensionResource(R .dimen.auto_complete_chip_margin)
81+ val chipMarginBottom = dimensionResource(R .dimen.auto_complete_chip_margin_bottom)
82+
83+ val interactionSource = remember { MutableInteractionSource () }
84+ val colors = OutlinedTextFieldDefaults .colors()
85+ val contentPadding =
86+ remember(chipContainerHeight, selectedOptions.size) {
87+ PaddingValues (
88+ start = 16 .dp,
89+ top =
90+ if (selectedOptions.isNotEmpty()) {
91+ with (density) { chipContainerHeight.toDp() } + 16 .dp
92+ } else {
93+ 16 .dp
94+ },
95+ end = 16 .dp,
96+ bottom = 16 .dp,
97+ )
98+ }
99+
67100 ExposedDropdownMenuBox (
68101 modifier = modifier,
69102 expanded = expanded,
70103 onExpandedChange = { expanded = it },
71104 ) {
72- OutlinedTextField (
73- value = autoCompleteText,
74- onValueChange = {
75- autoCompleteText = it
76- if (! expanded && autoCompleteText.text.isNotBlank()) expanded = true
77- },
78- modifier =
79- Modifier .fillMaxWidth()
80- .testTag(MULTI_AUTO_COMPLETE_TEXT_FIELD_TAG )
81- .semantics { if (isError) error(supportingText ? : " " ) }
82- .menuAnchor(ExposedDropdownMenuAnchorType .PrimaryEditable , enabled),
83- enabled = enabled,
84- isError = isError,
85- label = { labelText?.let { Text (it) } },
86- supportingText = { supportingText?.let { Text (it) } },
87- )
105+ Box {
106+ // Text field fills the parent and has content padding for chips
107+ BasicTextField (
108+ value = autoCompleteText,
109+ onValueChange = {
110+ autoCompleteText = it
111+ if (! expanded && autoCompleteText.text.isNotBlank()) expanded = true
112+ },
113+ modifier =
114+ Modifier .fillMaxWidth()
115+ .testTag(MULTI_AUTO_COMPLETE_TEXT_FIELD_TAG )
116+ .semantics { if (isError) error(supportingText ? : " " ) }
117+ .menuAnchor(ExposedDropdownMenuAnchorType .PrimaryEditable , enabled),
118+ enabled = enabled,
119+ textStyle = TextStyle .Default ,
120+ cursorBrush = SolidColor (colors.cursorColor),
121+ interactionSource = interactionSource,
122+ decorationBox = { innerTextField ->
123+ OutlinedTextFieldDefaults .DecorationBox (
124+ value = autoCompleteText.text,
125+ innerTextField = innerTextField,
126+ enabled = enabled,
127+ singleLine = false ,
128+ visualTransformation = VisualTransformation .None ,
129+ interactionSource = interactionSource,
130+ isError = isError,
131+ label = labelText?.let { { Text (it) } },
132+ supportingText = supportingText?.let { { Text (it) } },
133+ colors = colors,
134+ contentPadding = contentPadding,
135+ container = {
136+ OutlinedTextFieldDefaults .Container (
137+ enabled = enabled,
138+ isError = isError,
139+ interactionSource = interactionSource,
140+ colors = colors,
141+ )
142+ },
143+ )
144+ },
145+ )
146+
147+ // Chips overlay at the top of the text field
148+ if (selectedOptions.isNotEmpty()) {
149+ FlowRow (
150+ modifier =
151+ Modifier .fillMaxWidth()
152+ .padding(chipMargin)
153+ .padding(bottom = chipMarginBottom)
154+ .onSizeChanged { size -> chipContainerHeight = size.height },
155+ horizontalArrangement = Arrangement .spacedBy(8 .dp),
156+ ) {
157+ selectedOptions.forEach {
158+ InputChip (
159+ selected = false ,
160+ modifier = Modifier .testTag(MULTI_AUTO_COMPLETE_INPUT_CHIP_TAG ),
161+ enabled = enabled,
162+ onClick = { onOptionDeselected(it) },
163+ label = { Text (it.answerOptionAnnotatedString()) },
164+ trailingIcon = {
165+ Icon (
166+ painterResource(R .drawable.ic_clear),
167+ contentDescription = " Remove ${it.answerOptionString} " ,
168+ )
169+ },
170+ )
171+ }
172+ }
173+ }
174+ }
88175
89176 if (filteredOptions.isNotEmpty()) {
90177 ExposedDropdownMenu (expanded = expanded, onDismissRequest = { expanded = false }) {
91178 filteredOptions.forEach { option ->
92179 DropDownAnswerMenuItem (enabled, option) {
93- expanded = false
94180 autoCompleteText = TextFieldValue (" " ) // Reset autoComplete text to empty
95181 onNewOptionSelected(option)
182+ expanded = false
96183 }
97184 }
98185 }
99186 }
100-
101- FlowRow (
102- modifier =
103- Modifier .padding(dimensionResource(R .dimen.auto_complete_chip_margin))
104- .padding(bottom = dimensionResource(R .dimen.auto_complete_chip_margin_bottom)),
105- horizontalArrangement = Arrangement .spacedBy(8 .dp),
106- ) {
107- selectedOptions.forEach {
108- InputChip (
109- selected = false ,
110- modifier = Modifier .testTag(MULTI_AUTO_COMPLETE_INPUT_CHIP_TAG ),
111- enabled = enabled,
112- onClick = { onOptionDeselected(it) },
113- label = { Text (it.answerOptionAnnotatedString()) },
114- trailingIcon = {
115- Icon (painterResource(R .drawable.ic_clear), contentDescription = " Close" )
116- },
117- )
118- }
119- }
120187 }
121188}
122189
0 commit comments