From 3da61da45daadba73713ab66bfd040b54fbf1806 Mon Sep 17 00:00:00 2001 From: b-dev-mobile Date: Fri, 10 Jul 2026 02:58:13 +0000 Subject: [PATCH] =?UTF-8?q?design(mail):=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D0=B7=D0=B5=D1=80=201:1=20=D0=BF=D0=BE=20=D0=B6=D0=B8=D0=B2?= =?UTF-8?q?=D0=BE=D0=B9=20=D1=82=D0=B5=D0=BC=D0=B5=20=E2=80=94=20=D1=87?= =?UTF-8?q?=D0=B8=D0=BF=D1=8B=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _mobile-mail.css 3283–4067: - Поля Кому/Копия/Скрытая: подтверждённые адреса — ЧИПЫ-пилюли 26dp (фон #70B62B 12%, рамка 25%, текст 13/18, крестик 14 чёрный-50%), хвост — инлайн-ввод 14sp; значение остаётся строкой с запятыми, VM/parseRecipients не тронуты. Поле авто-высотой (FlowRow), minHeight 40. - Заголовок «Новое сообщение» 16/600 (был Bold titleMedium). - Поля темы/получателей: текст и плейсхолдер 14/500 (был 15). - Тулбар форматирования: r8 (был 10), фон #FDFDFD, тень темы, паддинг 4. Кнопка «Отправить» 44dp на всю ширину и скрепка 32 в углу редактора уже совпадали. Co-Authored-By: Claude Fable 5 --- .../f7cloud/feature/mail/MailComposeScreen.kt | 17 +-- .../feature/mail/MailRecipientComposeField.kt | 118 ++++++++++++++++-- 2 files changed, 114 insertions(+), 21 deletions(-) diff --git a/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailComposeScreen.kt b/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailComposeScreen.kt index 6b1a1aa..423b6e0 100644 --- a/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailComposeScreen.kt +++ b/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailComposeScreen.kt @@ -331,8 +331,8 @@ private fun MailComposeHeader( Column(modifier = Modifier.weight(1f).padding(horizontal = 8.dp)) { Text( title, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.SemiBold, maxLines = 1, overflow = TextOverflow.Ellipsis, ) @@ -457,11 +457,11 @@ private fun MailComposeField( value = value, onValueChange = onValueChange, modifier = Modifier.weight(1f), - textStyle = TextStyle(fontSize = 15.sp, color = F7Colors.TextPrimary), + textStyle = TextStyle(fontSize = 14.sp, lineHeight = 20.sp, color = F7Colors.TextPrimary), cursorBrush = SolidColor(F7Colors.Primary), decorationBox = { inner -> if (value.isEmpty()) { - Text(label, color = F7Colors.TextSecondary, fontSize = 15.sp) + Text(label, color = F7Colors.TextSecondary, fontSize = 14.sp) } inner() }, @@ -507,10 +507,11 @@ private fun MailComposeFormattingToolbar( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp) - .clip(RoundedCornerShape(10.dp)) - .border(1.dp, F7Colors.Border, RoundedCornerShape(10.dp)) - .background(Color.White) - .padding(8.dp), + .shadow(1.dp, RoundedCornerShape(8.dp), spotColor = F7Colors.Border) + .clip(RoundedCornerShape(8.dp)) + .border(1.dp, F7Colors.Border, RoundedCornerShape(8.dp)) + .background(Color(0xFFFDFDFD)) + .padding(4.dp), verticalArrangement = Arrangement.spacedBy(6.dp), ) { Row( diff --git a/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailRecipientComposeField.kt b/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailRecipientComposeField.kt index 20ded21..ddbf31a 100644 --- a/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailRecipientComposeField.kt +++ b/feature/mail/src/main/java/ru/forbion/f7cloud/feature/mail/MailRecipientComposeField.kt @@ -3,16 +3,24 @@ package ru.forbion.f7cloud.feature.mail import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row 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.layout.size +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -31,6 +39,13 @@ import ru.forbion.f7cloud.core.designsystem.F7Colors import ru.forbion.f7cloud.feature.contacts.ContactItem import ru.forbion.f7cloud.feature.contacts.ContactListRow +/** + * Поле получателей с чипами — как в живой теме (_mobile-mail.css): + * подтверждённые адреса (сегменты строки через запятую) — пилюли 26dp + * (фон #70B62B 12%, рамка 25%, текст 13/18, крестик 14), хвост — инлайн-ввод. + * Значение остаётся строкой «a, b, черновик» — VM/parseRecipients не меняются. + */ +@OptIn(ExperimentalLayoutApi::class) @Composable fun MailRecipientComposeField( label: String, @@ -43,6 +58,19 @@ fun MailRecipientComposeField( trailingIconUrl: String? = null, onTrailingClick: (() -> Unit)? = null, ) { + val parts = value.split(',').map { it.trim() } + val draft = if (value.trimEnd().endsWith(",")) "" else parts.lastOrNull().orEmpty() + val committed = (if (draft.isEmpty()) parts else parts.dropLast(1)).filter { it.isNotBlank() } + + fun rebuild(newCommitted: List, newDraft: String): String { + val head = newCommitted.joinToString(", ") + return when { + head.isEmpty() -> newDraft + newDraft.isEmpty() -> "$head, " + else -> "$head, $newDraft" + } + } + Box(modifier = Modifier.fillMaxWidth()) { Row( modifier = Modifier @@ -51,27 +79,55 @@ fun MailRecipientComposeField( .clip(RoundedCornerShape(8.dp)) .border(1.dp, F7Colors.Border, RoundedCornerShape(8.dp)) .background(Color.White) - .padding(horizontal = 12.dp, vertical = 10.dp), + .heightIn(min = 40.dp) + .padding(4.dp), verticalAlignment = Alignment.CenterVertically, ) { - BasicTextField( - value = value, - onValueChange = onValueChange, + FlowRow( modifier = Modifier.weight(1f), - textStyle = TextStyle(fontSize = 15.sp, color = F7Colors.TextPrimary), - cursorBrush = SolidColor(F7Colors.Primary), - decorationBox = { inner -> - if (value.isEmpty()) { - Text(label, color = F7Colors.TextSecondary, fontSize = 15.sp) - } - inner() - }, - ) + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(4.dp), + verticalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(4.dp), + ) { + committed.forEachIndexed { index, recipient -> + RecipientChip( + text = recipient, + onRemove = { + val rest = committed.toMutableList().also { it.removeAt(index) } + onValueChange(rebuild(rest, draft)) + }, + ) + } + BasicTextField( + value = draft, + onValueChange = { new -> onValueChange(rebuild(committed, new)) }, + modifier = Modifier + .widthIn(min = 80.dp) + .weight(1f) + .height(26.dp) + .padding(horizontal = 4.dp), + singleLine = true, + textStyle = TextStyle( + fontSize = 14.sp, + lineHeight = 20.sp, + color = F7Colors.TextPrimary, + ), + cursorBrush = SolidColor(F7Colors.Primary), + decorationBox = { inner -> + Box(contentAlignment = Alignment.CenterStart, modifier = Modifier.height(26.dp)) { + if (draft.isEmpty() && committed.isEmpty()) { + Text(label, color = F7Colors.TextSecondary, fontSize = 14.sp) + } + inner() + } + }, + ) + } if (trailingIconUrl != null && onTrailingClick != null) { AsyncImage( model = trailingIconUrl, contentDescription = null, modifier = Modifier + .padding(end = 4.dp) .size(16.dp) .clickable(onClick = onTrailingClick), contentScale = ContentScale.Fit, @@ -107,3 +163,39 @@ fun MailRecipientComposeField( } } } + +@Composable +private fun RecipientChip(text: String, onRemove: () -> Unit) { + Row( + modifier = Modifier + .height(26.dp) + .clip(RoundedCornerShape(100.dp)) + .background(F7Colors.Primary.copy(alpha = 0.12f)) + .border(1.dp, F7Colors.Primary.copy(alpha = 0.25f), RoundedCornerShape(100.dp)) + .padding(start = 6.dp, end = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(4.dp), + ) { + Text( + text, + fontSize = 13.sp, + lineHeight = 18.sp, + color = F7Colors.TextPrimary, + maxLines = 1, + overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis, + modifier = Modifier.widthIn(max = 220.dp), + ) + Icon( + imageVector = Icons.Filled.Close, + contentDescription = "Убрать получателя", + tint = F7Colors.TextPrimary.copy(alpha = 0.5f), + modifier = Modifier + .size(14.dp) + .clickable( + interactionSource = androidx.compose.runtime.remember { MutableInteractionSource() }, + indication = null, + onClick = onRemove, + ), + ) + } +}