package com.peachpasswords.ui.vault import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.PopupProperties import com.peachpasswords.ui.theme.LocalPeachAccent import com.peachpasswords.ui.theme.peachColors import com.peachpasswords.ui.theme.PeachType /** * Anchored suggestion dropdown for identity-style fields (username/email). * * Self-contained for clean merge into EntryEditScreen.kt. The brief uses * `DropdownMenu(expanded = focused && suggestions.isNotEmpty())`; we keep that * shape but mark the popup non-focusable so it never steals focus from the * BasicTextField that drives `expanded` — otherwise the field blurs the instant * the menu opens and the dropdown flickers shut. * * Mirrors the editor's visual language: IBM Plex Mono rows on the S2 glass * surface with a hairline T3 border. The popup has a bounded viewport so its * own Material scroll surface consumes list gestures instead of the editor's * parent scroll container. Suggestions are capped at eight by the ViewModel. */ @Composable fun IdentitySuggestDropdown( expanded: Boolean, suggestions: List, onPick: (String) -> Unit, onDismiss: () -> Unit, ) { DropdownMenu( expanded = expanded && suggestions.isNotEmpty(), onDismissRequest = onDismiss, properties = PopupProperties(focusable = false), modifier = Modifier .heightIn(max = 240.dp) .background(peachColors.S2) .border(1.dp, peachColors.T3, RoundedCornerShape(10.dp)) .clip(RoundedCornerShape(10.dp)), ) { suggestions.forEach { suggestion -> DropdownMenuItem( text = { Text( suggestion, style = TextStyle( color = peachColors.T1, fontFamily = PeachType.Mono, fontSize = 11.sp, ), ) }, onClick = { onPick(suggestion) }, modifier = Modifier.fillMaxWidth(), ) } } } /** * Identity field + anchored suggest dropdown, wired to focus state. * * Drop-in replacement for the bare `Field(...)` calls for username/email in * EntryEditScreen. It owns the focus tracking the dropdown needs; the parent * supplies value, suggestions, and the change callback. The dropdown is wrapped * with the field so the popup anchors directly beneath it. * * `Field` is `private` in EntryEditScreen.kt, so this re-implements the same * underline input rather than calling it. Keep this composable in the same file * when merging, or make `Field` accept `onFocusChanged` and reuse it (see * INTEGRATION.md "Option A"). */ @Composable fun IdentitySuggestField( label: String, value: String, suggestions: List, onChange: (String) -> Unit, joined: Boolean = false, divider: Boolean = false, ) { val accent = LocalPeachAccent.current var focused by remember { mutableStateOf(false) } Column( Modifier.fillMaxWidth() .then(if (joined) Modifier else Modifier.padding(bottom = 14.dp)) .then( if (joined) Modifier.padding(horizontal = 18.dp, vertical = 14.dp) else Modifier.border(1.dp, peachColors.T3, RoundedCornerShape(16.dp)).padding(horizontal = 18.dp, vertical = 14.dp), ), ) { Text(label.uppercase(), color = peachColors.T2, fontFamily = PeachType.Mono, fontSize = 9.sp) Spacer(Modifier.height(6.dp)) Box(Modifier.fillMaxWidth()) { BasicTextField( value = value, onValueChange = onChange, singleLine = true, textStyle = TextStyle(color = peachColors.T0, fontFamily = PeachType.Mono, fontSize = 13.sp), cursorBrush = SolidColor(accent), modifier = Modifier .fillMaxWidth() .onFocusChanged { focused = it.isFocused }, ) } IdentitySuggestDropdown( expanded = focused, suggestions = suggestions.filter { it != value }, onPick = { picked -> onChange(picked) focused = false }, onDismiss = { focused = false }, ) } if (joined && divider) Box(Modifier.fillMaxWidth().height(1.dp).background(peachColors.T3)) }