mirror of
https://github.com/openflocon/Flocon.git
synced 2026-04-28 09:59:35 +00:00
refact: [DASHBOARD] display list on left (#467)
Some checks are pending
docs / deploy (push) Waiting to run
Some checks are pending
docs / deploy (push) Waiting to run
Co-authored-by: Florent Champigny <florent@bere.al> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
parent
4a458ccb90
commit
8e74ae689a
4 changed files with 238 additions and 173 deletions
|
|
@ -6,6 +6,7 @@
|
|||
<string name="close">Close</string>
|
||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
<string name="crashes">Crashes</string>
|
||||
<string name="dashboards">Dashboards</string>
|
||||
<string name="dashboard_action_delete">Delete Dashboards</string>
|
||||
<string name="dashboard_empty">No dashboard</string>
|
||||
<string name="dashboard_removed">Dashboard removed</string>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import flocondesktop.composeapp.generated.resources.Res
|
||||
import flocondesktop.composeapp.generated.resources.dashboard_action_delete
|
||||
|
|
@ -78,50 +84,60 @@ fun DashboardScreen(
|
|||
FloconFeature(
|
||||
modifier = modifier
|
||||
) {
|
||||
FloconPageTopBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selector = {
|
||||
DashboardSelectorView(
|
||||
dashboardsState = deviceDashboards,
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
FloconPageTopBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selector = null,
|
||||
filterBar = {
|
||||
Box(modifier = Modifier.weight(1f)) // to have actions on the right
|
||||
},
|
||||
actions = {
|
||||
DashboardArrangementView(
|
||||
onArrangementClicked = onArrangementClicked,
|
||||
arrangement = arrangement
|
||||
)
|
||||
|
||||
FloconOverflow {
|
||||
FloconDropdownMenuItem(
|
||||
text = stringResource(Res.string.dashboard_action_delete),
|
||||
leadingIcon = Icons.Outlined.Delete,
|
||||
onClick = { deleteCurrentDashboard() }
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(Modifier.fillMaxSize()) {
|
||||
DashboardSidebarView(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(340.dp),
|
||||
state = deviceDashboards,
|
||||
onDashboardSelected = onDashboardSelected,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onDeleteClicked = onDeleteClicked,
|
||||
)
|
||||
},
|
||||
filterBar = {
|
||||
Box(modifier = Modifier.weight(1f)) // to have actions on the right
|
||||
},
|
||||
actions = {
|
||||
DashboardArrangementView(
|
||||
onArrangementClicked = onArrangementClicked,
|
||||
arrangement = arrangement
|
||||
)
|
||||
|
||||
FloconOverflow {
|
||||
FloconDropdownMenuItem(
|
||||
text = stringResource(Res.string.dashboard_action_delete),
|
||||
leadingIcon = Icons.Outlined.Delete,
|
||||
onClick = { deleteCurrentDashboard() }
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
state?.let {
|
||||
DashboardView(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(FloconTheme.shapes.medium)
|
||||
.background(FloconTheme.colorPalette.primary)
|
||||
.padding(all = 8.dp),
|
||||
viewState = state,
|
||||
onClickButton = onClickButton,
|
||||
submitTextField = submitTextField,
|
||||
submitForm = submitForm,
|
||||
onUpdateCheckBox = onUpdateCheckBox,
|
||||
arrangement = arrangement,
|
||||
onOpenExternalClicked = onOpenExternalClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
state?.let {
|
||||
DashboardView(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(FloconTheme.shapes.medium)
|
||||
.background(FloconTheme.colorPalette.primary)
|
||||
.padding(all = 8.dp),
|
||||
viewState = state,
|
||||
onClickButton = onClickButton,
|
||||
submitTextField = submitTextField,
|
||||
submitForm = submitForm,
|
||||
onUpdateCheckBox = onUpdateCheckBox,
|
||||
arrangement = arrangement,
|
||||
onOpenExternalClicked = onOpenExternalClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
package io.github.openflocon.flocondesktop.features.dashboard.view
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.KeyboardArrowDown
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import flocondesktop.composeapp.generated.resources.Res
|
||||
import flocondesktop.composeapp.generated.resources.dashboard_empty
|
||||
import io.github.openflocon.flocondesktop.features.dashboard.model.DashboardsStateUiModel
|
||||
import io.github.openflocon.flocondesktop.features.dashboard.model.DeviceDashboardUiModel
|
||||
import io.github.openflocon.library.designsystem.FloconTheme
|
||||
import io.github.openflocon.library.designsystem.components.DropdownFilterFieldView
|
||||
import io.github.openflocon.library.designsystem.components.FloconButton
|
||||
import io.github.openflocon.library.designsystem.components.FloconDropdownMenu
|
||||
import io.github.openflocon.library.designsystem.components.FloconDropdownMenuItem
|
||||
import io.github.openflocon.library.designsystem.components.FloconIcon
|
||||
import io.github.openflocon.library.designsystem.components.FloconLinearProgressIndicator
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Composable
|
||||
internal fun DashboardSelectorView(
|
||||
dashboardsState: DashboardsStateUiModel,
|
||||
onDashboardSelected: (DeviceDashboardUiModel) -> Unit,
|
||||
onDeleteClicked: (DeviceDashboardUiModel) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
FloconDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
onExpandRequest = {
|
||||
if (dashboardsState is DashboardsStateUiModel.WithContent) expanded = true
|
||||
},
|
||||
anchorContent = {
|
||||
FloconButton(
|
||||
onClick = {
|
||||
if (dashboardsState is DashboardsStateUiModel.WithContent) expanded = true
|
||||
}, containerColor = FloconTheme.colorPalette.secondary
|
||||
) {
|
||||
when (dashboardsState) {
|
||||
DashboardsStateUiModel.Empty -> Text(
|
||||
stringResource(Res.string.dashboard_empty),
|
||||
style = FloconTheme.typography.bodySmall
|
||||
)
|
||||
|
||||
DashboardsStateUiModel.Loading -> FloconLinearProgressIndicator()
|
||||
is DashboardsStateUiModel.WithContent -> {
|
||||
Text(
|
||||
text = dashboardsState.selected.id,
|
||||
style = FloconTheme.typography.bodySmall
|
||||
)
|
||||
FloconIcon(
|
||||
imageVector = Icons.Outlined.KeyboardArrowDown,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
) {
|
||||
var filterText by remember { mutableStateOf("") }
|
||||
DropdownFilterFieldView(
|
||||
value = filterText,
|
||||
onValueChanged = {
|
||||
filterText = it
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
if (dashboardsState is DashboardsStateUiModel.WithContent) {
|
||||
val filteredItems = remember(filterText, dashboardsState.dashboards) {
|
||||
dashboardsState.dashboards.filter {
|
||||
it.id.contains(
|
||||
filterText,
|
||||
ignoreCase = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
filteredItems.fastForEach { dashboard ->
|
||||
FloconDropdownMenuItem(
|
||||
text = dashboard.id,
|
||||
onClick = {
|
||||
onDashboardSelected(dashboard)
|
||||
expanded = false
|
||||
},
|
||||
secondaryAction = {
|
||||
Box(
|
||||
Modifier.clip(RoundedCornerShape(4.dp))
|
||||
.background(
|
||||
Color.White.copy(alpha = 0.8f)
|
||||
).padding(2.dp).clickable {
|
||||
onDeleteClicked(dashboard)
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
FloconIcon(
|
||||
imageVector = Icons.Outlined.Close,
|
||||
tint = FloconTheme.colorPalette.primary,
|
||||
modifier = Modifier.size(14.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package io.github.openflocon.flocondesktop.features.dashboard.view
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.Dashboard
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.composeunstyled.Text
|
||||
import flocondesktop.composeapp.generated.resources.Res
|
||||
import flocondesktop.composeapp.generated.resources.dashboards
|
||||
import flocondesktop.composeapp.generated.resources.filter
|
||||
import io.github.openflocon.flocondesktop.features.dashboard.model.DashboardsStateUiModel
|
||||
import io.github.openflocon.flocondesktop.features.dashboard.model.DeviceDashboardUiModel
|
||||
import io.github.openflocon.flocondesktop.features.network.list.view.components.FilterBar
|
||||
import io.github.openflocon.library.designsystem.FloconTheme
|
||||
import io.github.openflocon.library.designsystem.components.FloconIcon
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Composable
|
||||
fun DashboardSidebarView(
|
||||
modifier: Modifier = Modifier,
|
||||
state: DashboardsStateUiModel,
|
||||
onDashboardSelected: (DeviceDashboardUiModel) -> Unit,
|
||||
onDeleteClicked: (DeviceDashboardUiModel) -> Unit,
|
||||
) {
|
||||
val borderColor = FloconTheme.colorPalette.secondary
|
||||
|
||||
Surface(
|
||||
color = FloconTheme.colorPalette.primary,
|
||||
modifier = modifier
|
||||
.clip(FloconTheme.shapes.medium)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = borderColor,
|
||||
shape = FloconTheme.shapes.medium
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(all = 4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(Res.string.dashboards),
|
||||
color = FloconTheme.colorPalette.onSurface,
|
||||
style = FloconTheme.typography.bodyMedium.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
)
|
||||
when (state) {
|
||||
DashboardsStateUiModel.Empty -> Unit
|
||||
DashboardsStateUiModel.Loading -> Unit
|
||||
is DashboardsStateUiModel.WithContent -> {
|
||||
var filterText = remember { mutableStateOf("") }
|
||||
val filteredDashboards = remember(filterText.value, state.dashboards) {
|
||||
val filterTextValue = filterText.value
|
||||
if (filterTextValue.isBlank()) {
|
||||
state.dashboards
|
||||
} else {
|
||||
state.dashboards.filter {
|
||||
it.id.contains(filterTextValue, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
FilterBar(
|
||||
filterText = filterText,
|
||||
placeholderText = stringResource(Res.string.filter),
|
||||
onTextChange = {
|
||||
filterText.value = it
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp).padding(bottom = 12.dp),
|
||||
)
|
||||
filteredDashboards.forEach {
|
||||
DashboardItemView(
|
||||
dashboard = it,
|
||||
onSelect = {
|
||||
onDashboardSelected(it)
|
||||
},
|
||||
onDelete = {
|
||||
onDeleteClicked(it)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isSelected = it.id == state.selected.id
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DashboardItemView(
|
||||
dashboard: DeviceDashboardUiModel,
|
||||
isSelected: Boolean,
|
||||
onSelect: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val (background, textColor) = if (isSelected) {
|
||||
FloconTheme.colorPalette.accent.copy(alpha = 0.4f) to FloconTheme.colorPalette.onAccent
|
||||
} else {
|
||||
Color.Transparent to FloconTheme.colorPalette.onSurface
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(background)
|
||||
.clickable(onClick = onSelect)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.width(14.dp),
|
||||
imageVector = Icons.Outlined.Dashboard,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(textColor),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
dashboard.id,
|
||||
color = textColor,
|
||||
style = FloconTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
Box(
|
||||
Modifier.clip(RoundedCornerShape(4.dp))
|
||||
.background(
|
||||
Color.White.copy(alpha = 0.1f)
|
||||
).clickable {
|
||||
onDelete()
|
||||
}.padding(2.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
FloconIcon(
|
||||
imageVector = Icons.Outlined.Close,
|
||||
tint = textColor.copy(alpha = 0.6f),
|
||||
modifier = Modifier.size(14.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue