files: рескин панели свойств/доступа под макет (табы-пилюли, шапка, звезда)
Правая панель (FilesDetailsSheet) по макету «Правая панель свойства/доступ»: - табы из текст+подчёркивание → две иконки-пилюли (Обсуждение 💬 / Доступ 👥), активная — зелёная заливка PrimaryLight; - шапка: крестик в правом верхнем углу, чип владельца (аватар-инициал+логин), дата/размер справа, звезда-избранное (Icons Star/StarBorder, wired на vm.toggleFavorite) вместо строки «Владелец … • …»; - кнопка отправки комментария → зелёный круг-градиент с белой стрелкой (было «→»). Иконки — Material (extended уже в модуле). Компилируется.
This commit is contained in:
@@ -21,9 +21,18 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.ChatBubbleOutline
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Group
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material.icons.filled.StarBorder
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
@@ -49,6 +58,7 @@ import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.PlatformTextStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.boundsInWindow
|
||||
@@ -998,10 +1008,10 @@ fun FilesDetailsSheet(
|
||||
loading: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onTabChange: (FileDetailsTab) -> Unit,
|
||||
onToggleFavorite: () -> Unit,
|
||||
onWebOnlyAction: (String) -> Unit,
|
||||
) {
|
||||
if (item == null) return
|
||||
val base = serverUrl.trimEnd('/')
|
||||
var commentDraft by remember(item.relativePath) { mutableStateOf("") }
|
||||
androidx.compose.ui.window.Dialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
@@ -1016,9 +1026,26 @@ fun FilesDetailsSheet(
|
||||
.fillMaxSize()
|
||||
.padding(20.dp),
|
||||
) {
|
||||
// Крестик закрытия — правый верхний угол (по макету)
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable(onClick = onDismiss),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Filled.Close,
|
||||
contentDescription = "Закрыть",
|
||||
tint = F7Colors.TextSecondary,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
@@ -1029,52 +1056,86 @@ fun FilesDetailsSheet(
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Spacer(Modifier.height(6.dp))
|
||||
val metaParts = buildList {
|
||||
item.size?.let { add(formatFileSize(it)) }
|
||||
item.lastModified?.let {
|
||||
add(
|
||||
SimpleDateFormat("d MMMM", Locale("ru")).format(Date(it)),
|
||||
Spacer(Modifier.height(8.dp))
|
||||
// Чип владельца (аватар-инициал + логин) — по макету
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(100.dp))
|
||||
.background(F7Colors.SurfaceMuted)
|
||||
.padding(start = 4.dp, end = 10.dp, top = 4.dp, bottom = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clip(CircleShape)
|
||||
.background(F7Colors.PrimaryLight),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
ownerLabel.firstOrNull()?.uppercaseChar()?.toString() ?: "?",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = F7Colors.Primary,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
}
|
||||
add("Владелец $ownerLabel")
|
||||
}
|
||||
Text(
|
||||
metaParts.joinToString(" • "),
|
||||
ownerLabel,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = F7Colors.TextSecondary,
|
||||
maxLines = 2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("✕", style = MaterialTheme.typography.titleMedium)
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
val dateSize = buildList {
|
||||
item.lastModified?.let {
|
||||
add(SimpleDateFormat("d MMMM", Locale("ru")).format(Date(it)))
|
||||
}
|
||||
item.size?.let { add(formatFileSize(it)) }
|
||||
}
|
||||
if (dateSize.isNotEmpty()) {
|
||||
Text(
|
||||
dateSize.joinToString(" / "),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = F7Colors.TextSecondary,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
Icon(
|
||||
if (item.favorite) Icons.Filled.Star else Icons.Filled.StarBorder,
|
||||
contentDescription = "Избранное",
|
||||
tint = if (item.favorite) F7Colors.Primary else F7Colors.TextSecondary,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable(onClick = onToggleFavorite),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
FilesDetailsTabButton(
|
||||
serverUrl = serverUrl,
|
||||
label = "Общий доступ",
|
||||
iconPath = "files/many-person-gray.svg",
|
||||
selected = tab == FileDetailsTab.Sharing,
|
||||
onClick = { onTabChange(FileDetailsTab.Sharing) },
|
||||
)
|
||||
FilesDetailsTabButton(
|
||||
serverUrl = serverUrl,
|
||||
label = "События",
|
||||
iconPath = "files/grid-files-gray.svg",
|
||||
FilesDetailsTabPill(
|
||||
icon = Icons.Filled.ChatBubbleOutline,
|
||||
contentDescription = "Обсуждение",
|
||||
selected = tab == FileDetailsTab.Events,
|
||||
onClick = { onTabChange(FileDetailsTab.Events) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
FilesDetailsTabPill(
|
||||
icon = Icons.Filled.Group,
|
||||
contentDescription = "Общий доступ",
|
||||
selected = tab == FileDetailsTab.Sharing,
|
||||
onClick = { onTabChange(FileDetailsTab.Sharing) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
color = F7Colors.Border.copy(alpha = 0.5f),
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
@@ -1117,42 +1178,28 @@ fun FilesDetailsSheet(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FilesDetailsTabButton(
|
||||
serverUrl: String,
|
||||
label: String,
|
||||
iconPath: String,
|
||||
private fun FilesDetailsTabPill(
|
||||
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
contentDescription: String,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val base = serverUrl.trimEnd('/')
|
||||
Column(
|
||||
modifier = Modifier.clickable(onClick = onClick),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
AsyncImage(
|
||||
model = "$base/themes/forbion/images/$iconPath",
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentScale = ContentScale.Fit,
|
||||
)
|
||||
Text(
|
||||
label,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium,
|
||||
color = if (selected) F7Colors.TextPrimary else F7Colors.TextSecondary,
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
val shape = RoundedCornerShape(14.dp)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(2.dp)
|
||||
.width(72.dp)
|
||||
.clip(RoundedCornerShape(1.dp))
|
||||
.background(if (selected) F7Colors.Primary else Color.Transparent),
|
||||
modifier = modifier
|
||||
.height(40.dp)
|
||||
.clip(shape)
|
||||
.background(if (selected) F7Colors.PrimaryLight else F7Colors.Surface)
|
||||
.border(1.dp, if (selected) Color.Transparent else F7Colors.Border, shape)
|
||||
.clickable(onClick = onClick),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = if (selected) F7Colors.Primary else F7Colors.TextSecondary,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1348,8 +1395,24 @@ private fun FilesEventsTabContent(
|
||||
modifier = Modifier.weight(1f),
|
||||
onEnter = onSubmitComment,
|
||||
)
|
||||
TextButton(onClick = onSubmitComment) {
|
||||
Text("→", fontSize = 20.sp)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
listOf(F7Colors.PrimaryGradientStart, F7Colors.PrimaryGradientEnd),
|
||||
),
|
||||
)
|
||||
.clickable(onClick = onSubmitComment),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.ArrowForward,
|
||||
contentDescription = "Отправить",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
|
||||
@@ -360,6 +360,7 @@ fun FilesScreen(
|
||||
loading = state.detailsLoading,
|
||||
onDismiss = vm::hideDetails,
|
||||
onTabChange = vm::setDetailsTab,
|
||||
onToggleFavorite = { state.detailsItem?.let { vm.toggleFavorite(session, it) } },
|
||||
onWebOnlyAction = vm::showWebOnlyMessage,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user