Skip to content

Commit 384dbc4

Browse files
committed
Set textfield content paddingTop within bounds of the decorationBox
1 parent 8d31f4b commit 384dbc4

File tree

1 file changed

+105
-38
lines changed

1 file changed

+105
-38
lines changed

datacapture/src/main/java/com/google/android/fhir/datacapture/views/compose/MultiAutoCompleteTextItem.kt

Lines changed: 105 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,40 @@
1616

1717
package com.google.android.fhir.datacapture.views.compose
1818

19+
import androidx.compose.foundation.interaction.MutableInteractionSource
1920
import androidx.compose.foundation.layout.Arrangement
21+
import androidx.compose.foundation.layout.Box
2022
import androidx.compose.foundation.layout.FlowRow
23+
import androidx.compose.foundation.layout.PaddingValues
2124
import androidx.compose.foundation.layout.fillMaxWidth
2225
import androidx.compose.foundation.layout.padding
26+
import androidx.compose.foundation.text.BasicTextField
2327
import androidx.compose.material3.ExperimentalMaterial3Api
2428
import androidx.compose.material3.ExposedDropdownMenuAnchorType
2529
import androidx.compose.material3.ExposedDropdownMenuBox
2630
import androidx.compose.material3.Icon
2731
import androidx.compose.material3.InputChip
28-
import androidx.compose.material3.OutlinedTextField
32+
import androidx.compose.material3.OutlinedTextFieldDefaults
2933
import androidx.compose.material3.Text
3034
import androidx.compose.runtime.Composable
3135
import androidx.compose.runtime.getValue
36+
import androidx.compose.runtime.mutableIntStateOf
3237
import androidx.compose.runtime.mutableStateOf
3338
import androidx.compose.runtime.remember
3439
import androidx.compose.runtime.setValue
3540
import 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
3644
import androidx.compose.ui.platform.testTag
3745
import androidx.compose.ui.res.dimensionResource
3846
import androidx.compose.ui.res.painterResource
3947
import androidx.compose.ui.semantics.error
4048
import androidx.compose.ui.semantics.semantics
4149
import androidx.compose.ui.text.AnnotatedString
50+
import androidx.compose.ui.text.TextStyle
4251
import androidx.compose.ui.text.input.TextFieldValue
52+
import androidx.compose.ui.text.input.VisualTransformation
4353
import androidx.compose.ui.unit.dp
4454
import com.google.android.fhir.datacapture.R
4555
import 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

Comments
 (0)