refactor(ui): extract shared ExposedDropdownField (issue #796) (#815)

DropdownField (ConfigScreen) and PluginProviderDropdown (PluginsScreen)
were byte-for-byte identical. Extract into ui/common/ExposedDropdownField
and route both call sites to it. No behavior change.

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-04 20:39:23 +03:00
1 parent e639e33188
commit 509a018434
3 files changed
+74 -103

No files matched your search

@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.InstallDesktop
@@ -24,11 +23,7 @@ import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -56,6 +51,7 @@ import com.m57.hermescontrol.theme.LocalHermesStatusColors
import com.m57.hermescontrol.ui.common.DetailDialog
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.ExposedDropdownField
import com.m57.hermescontrol.ui.common.HermesScaffold
import com.m57.hermescontrol.ui.common.NavIcon
import com.m57.hermescontrol.ui.common.SearchBar
@@ -247,7 +243,7 @@ private fun ProviderSelectionSection(
style = MaterialTheme.typography.labelMedium,
)
Spacer(modifier = Modifier.height(4.dp))
PluginProviderDropdown(
ExposedDropdownField(
label = stringResource(R.string.plugins_memory_provider_label),
options =
listOf(
@@ -276,7 +272,7 @@ private fun ProviderSelectionSection(
style = MaterialTheme.typography.labelMedium,
)
Spacer(modifier = Modifier.height(4.dp))
PluginProviderDropdown(
ExposedDropdownField(
label = stringResource(R.string.plugins_context_engine_label),
options = listOf("compressor") + state.contextOptions.map { it.name },
selectedValue = state.contextEngine,
@@ -302,51 +298,6 @@ private fun ProviderSelectionSection(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun PluginProviderDropdown(
label: String,
options: List<String>,
selectedValue: String,
onOptionSelected: (String) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
) {
OutlinedTextField(
value = selectedValue.ifEmpty { options.firstOrNull() ?: "" },
onValueChange = {},
readOnly = true,
modifier =
Modifier
.fillMaxWidth()
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
label = { Text(label) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
singleLine = true,
shape = RoundedCornerShape(8.dp),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
options.forEach { option ->
DropdownMenuItem(
text = { Text(option) },
onClick = {
onOptionSelected(option)
expanded = false
},
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun InstallSection(