From db0d8c9e918eb3015d745ac76af15ef3847e3c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20T=C3=B3th?= <tothmiklostibor@gmail.com> Date: Tue, 22 Feb 2022 16:46:30 +0100 Subject: [PATCH] Improve UI --- mosogepsch/src/jsMain/kotlin/Main.kt | 20 ++- .../src/jsMain/kotlin/components/Floor.kt | 160 +----------------- .../src/jsMain/kotlin/components/FloorCard.kt | 50 ++++++ .../src/jsMain/kotlin/components/Icon.kt | 25 +++ .../src/jsMain/kotlin/components/UnderCard.kt | 50 ++++++ .../src/jsMain/kotlin/components/Utils.kt | 44 +++++ .../src/jsMain/kotlin/localization/English.kt | 4 +- .../jsMain/kotlin/localization/Hungarian.kt | 3 + .../src/jsMain/kotlin/styles/FloorStyle.kt | 68 ++++++++ mosogepsch/src/jsMain/resources/index.html | 1 + 10 files changed, 267 insertions(+), 158 deletions(-) create mode 100644 mosogepsch/src/jsMain/kotlin/components/FloorCard.kt create mode 100644 mosogepsch/src/jsMain/kotlin/components/Icon.kt create mode 100644 mosogepsch/src/jsMain/kotlin/components/UnderCard.kt create mode 100644 mosogepsch/src/jsMain/kotlin/components/Utils.kt create mode 100644 mosogepsch/src/jsMain/kotlin/styles/FloorStyle.kt diff --git a/mosogepsch/src/jsMain/kotlin/Main.kt b/mosogepsch/src/jsMain/kotlin/Main.kt index b9548d9..00ef8be 100644 --- a/mosogepsch/src/jsMain/kotlin/Main.kt +++ b/mosogepsch/src/jsMain/kotlin/Main.kt @@ -1,4 +1,5 @@ import androidx.compose.runtime.* +import api.MachineKind import app.softwork.bootstrapcompose.* import app.softwork.bootstrapcompose.Color import components.* @@ -12,6 +13,7 @@ import org.jetbrains.compose.web.renderComposable import org.w3c.dom.HTMLDivElement import org.w3c.dom.get import org.w3c.dom.set +import styles.FloorStyle import styles.LocalStyle import styles.Style @@ -47,7 +49,23 @@ fun main() { DomSideEffect { it.setImportantBg(mosogepStyle.colors.surface) } - Text("MosógépSCH") + Div( + attrs = { + style { + display(DisplayStyle.Flex) + flexDirection(FlexDirection.Row) + gap(.5.em) + alignItems(AlignItems.Center) + } + } + ) { + icon( + src = MachineKind.Washer.toIcon(), + color = mosogepStyle.colors.onSurface, + width = 1.5.em + ) + Text("MosógépSCH") + } switch( label = "Sötét téma", value = mosogepStyle.dark, diff --git a/mosogepsch/src/jsMain/kotlin/components/Floor.kt b/mosogepsch/src/jsMain/kotlin/components/Floor.kt index 51a0a87..068dac2 100644 --- a/mosogepsch/src/jsMain/kotlin/components/Floor.kt +++ b/mosogepsch/src/jsMain/kotlin/components/Floor.kt @@ -1,104 +1,14 @@ package components -import styles.LocalStyle import androidx.compose.runtime.* import api.Floor import api.Machine -import api.MachineKind -import api.MachineStatus -import localization.LocalLang -import localization.machineKind -import localization.machineStatus import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.dom.* -import styles.ColorPair -import styles.applyColorPair - -object FloorStyle: StyleSheet() { - val container by style { - display(DisplayStyle.Flex) - flexDirection(FlexDirection.Column) - justifyContent(JustifyContent.SpaceAround) - alignItems(AlignItems.Center) - position(Position.Relative) - } - - val card by style { - maxWidth(20.em) - width(20.em) - flexDirection(FlexDirection.Row) - justifyContent(JustifyContent.SpaceAround) - property("box-shadow", "0 0 .7em #00000033") - } - - val floorNum by style { - minWidth(2.75.ch) - marginBottom(0.px) - textAlign("right") - } - - val machine by style { - flexGrow(1) - cursor("pointer") - } - - val absCnt by style { - position(Position.Absolute) - left(0.px) - bottom(4.em) - width(0.px) - height(0.px) - property("z-index", -1) - } - val relCnt by style { - position(Position.Relative) - width(0.px) - height(0.px) - } - val underflowCnt by style { - margin(0.px) - padding(1.em) - paddingTop(0.em) - width(22.em) - overflow("hidden") - property("transition", "${styles.Style.baseTransition}, max-height 0.75s") - } - val underflowCard by style { - justifyContent(JustifyContent.SpaceBetween) - flexDirection(FlexDirection.Row) - gap(1.5.em) - padding(1.5.em) - margin(0.px) - width(100.percent) - paddingTop(5.em) - property("transition", "${styles.Style.baseTransition}, box-shadow 0.5s") - property("box-shadow", "0 0 1em #00000044") - } - val infoDiv by style { - display(DisplayStyle.Flex) - flexDirection(FlexDirection.Column) - flexGrow(1) - } -} +import styles.FloorStyle @Composable fun floor(floor: Floor, selectedMachine: Machine?, setMachine: (Machine?)->Unit) { - - val lang = LocalLang.current - val colors = LocalStyle.current.colors - fun MachineStatus.toColor(): ColorPair = when (this) { - MachineStatus.Available -> ColorPair(colors.primary, colors.onPrimary) - MachineStatus.NotAvailable -> ColorPair(colors.error, colors.onError) - } - fun MachineStatus.toUnderColor(): ColorPair = when (this) { - MachineStatus.Available -> ColorPair(colors.primaryContainer, colors.onPrimaryContainer) - MachineStatus.NotAvailable -> ColorPair(colors.errorContainer, colors.onErrorContainer) - } - fun MachineKind.toIcon(): String = when (this) { - MachineKind.Washer -> "washer.svg" - MachineKind.Dryer -> "fan.svg" - } - val zIndex = 200 - floor.id*2 Div(attrs = { @@ -107,43 +17,7 @@ fun floor(floor: Floor, selectedMachine: Machine?, setMachine: (Machine?)->Unit) property("z-index", zIndex) } }) { - card(attrs = { - classes(FloorStyle.card) - style { - backgroundColor(colors.surfaceVariant) - color(colors.onSurfaceVariant) - } - }) { - H3(attrs = { - classes(FloorStyle.floorNum) - }) { Text("${floor.id}.") } - floor.machines.forEach { machine -> - card(attrs = { - classes(FloorStyle.machine) - style { - applyColorPair(machine.status.toColor()) - } - onClick { - if (selectedMachine == machine) { - setMachine(null) - } else { - setMachine(machine) - } - } - }) { - Div( - attrs = { - style { - backgroundColor(machine.status.toColor().fg) - property("mask", "url(/${machine.kindOf.toIcon()}) no-repeat center / contain") - height(2.em) - width(2.em) - } - } - ) {} - } - } - } + FloorCard(floor, selectedMachine, setMachine) floor.machines.forEach { machine -> Div(attrs = { @@ -156,39 +30,13 @@ fun floor(floor: Floor, selectedMachine: Machine?, setMachine: (Machine?)->Unit) classes(FloorStyle.underflowCnt) style { if (selectedMachine == machine) { - maxHeight(20.em) + maxHeight(22.em) } else { maxHeight(0.px) } } }) { - card(attrs = { - classes(FloorStyle.underflowCard) - style { - applyColorPair(machine.status.toUnderColor()) - if (selectedMachine != machine) { - property("box-shadow", "none") - } - } - }) { - Div( - attrs = { - style { - backgroundColor(machine.status.toUnderColor().fg) - property("mask", "url(/${machine.kindOf.toIcon()}) no-repeat center / contain") - height(2.em) - width(2.em) - } - } - ) {} - Div(attrs = { - classes(FloorStyle.infoDiv) - }) { - H5 { Text("${lang.machineKind(machine.kindOf)} ${lang.floorFormat(floor.id)}") } - P { Text("${lang.lastUpdated} ${lang.formatInstant(machine.lastQueryTime)}") } - P { Text(lang.machineStatus(machine.status)) } - } - } + underCard(floor, machine, selectedMachine) } } } diff --git a/mosogepsch/src/jsMain/kotlin/components/FloorCard.kt b/mosogepsch/src/jsMain/kotlin/components/FloorCard.kt new file mode 100644 index 0000000..b37c80a --- /dev/null +++ b/mosogepsch/src/jsMain/kotlin/components/FloorCard.kt @@ -0,0 +1,50 @@ +package components + +import androidx.compose.runtime.Composable +import api.Floor +import api.Machine +import org.jetbrains.compose.web.css.* +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.H3 +import org.jetbrains.compose.web.dom.Text +import styles.FloorStyle +import styles.LocalStyle +import styles.applyColorPair + +@Composable +fun FloorCard(floor: Floor, selectedMachine: Machine?, setMachine: (Machine?)->Unit) { + val colors = LocalStyle.current.colors + card(attrs = { + classes(FloorStyle.card) + style { + backgroundColor(colors.surfaceVariant) + color(colors.onSurfaceVariant) + } + }) { + H3(attrs = { + classes(FloorStyle.floorNum) + }) { Text("${floor.id}.") } + floor.machines.forEach { machine -> + val cardColor = machine.status.toColor() + card(attrs = { + classes(FloorStyle.machine) + style { + applyColorPair(cardColor) + } + onClick { + if (selectedMachine == machine) { + setMachine(null) + } else { + setMachine(machine) + } + } + }) { + icon( + src = machine.kindOf.toIcon(), + color = cardColor.fg, + width = 2.em + ) + } + } + } +} \ No newline at end of file diff --git a/mosogepsch/src/jsMain/kotlin/components/Icon.kt b/mosogepsch/src/jsMain/kotlin/components/Icon.kt new file mode 100644 index 0000000..cb14beb --- /dev/null +++ b/mosogepsch/src/jsMain/kotlin/components/Icon.kt @@ -0,0 +1,25 @@ +package components + +import androidx.compose.runtime.Composable +import org.jetbrains.compose.web.css.* +import org.jetbrains.compose.web.dom.Div + +@Composable +fun icon( + src: String, + color: CSSColorValue, + width: CSSNumeric, + height: CSSNumeric = width, +) { + Div( + attrs = { + style { + backgroundColor(color) + property("mask", "url($src) no-repeat center / contain") + property("-webkit-mask", "url($src) no-repeat center / contain") + height(height) + width(width) + } + } + ) {} +} \ No newline at end of file diff --git a/mosogepsch/src/jsMain/kotlin/components/UnderCard.kt b/mosogepsch/src/jsMain/kotlin/components/UnderCard.kt new file mode 100644 index 0000000..aa67567 --- /dev/null +++ b/mosogepsch/src/jsMain/kotlin/components/UnderCard.kt @@ -0,0 +1,50 @@ +package components + +import androidx.compose.runtime.* +import api.Floor +import api.Machine +import localization.LocalLang +import localization.machineKind +import localization.machineStatus +import org.jetbrains.compose.web.dom.H5 +import org.jetbrains.compose.web.dom.P +import org.jetbrains.compose.web.dom.Text +import styles.ColorPair +import styles.FloorStyle +import styles.LocalStyle +import styles.applyColorPair + + + +@Composable +fun underCard(floor: Floor, machine: Machine, selectedMachine: Machine?) { + val lang = LocalLang.current + val cardColor = machine.status.toUnderColor() + val subbedColor = subbedColor() + val unsubbedColor = unsubbedColor() + + card(attrs = { + classes(FloorStyle.underflowCard) + style { + applyColorPair(cardColor) + if (selectedMachine != machine) { + property("box-shadow", "none") + } + } + }) { + H5 { Text("${lang.machineKind(machine.kindOf)} ${lang.floorFormat(floor.id)}") } + P { Text("${lang.lastUpdated} ${lang.formatInstant(machine.lastQueryTime)}") } + P { Text(lang.machineStatus(machine.status)) } + var subbed by remember { mutableStateOf(false) } + card(attrs = { + classes(FloorStyle.subBtn) + onClick { subbed = !subbed } + style { + applyColorPair(if (subbed) subbedColor else unsubbedColor) + } + }) { + // TODO place icon + Text(if (subbed) lang.unsub else lang.sub) + } + } +} \ No newline at end of file diff --git a/mosogepsch/src/jsMain/kotlin/components/Utils.kt b/mosogepsch/src/jsMain/kotlin/components/Utils.kt new file mode 100644 index 0000000..187121d --- /dev/null +++ b/mosogepsch/src/jsMain/kotlin/components/Utils.kt @@ -0,0 +1,44 @@ +package components + +import androidx.compose.runtime.Composable +import api.MachineKind +import api.MachineStatus +import styles.ColorPair +import styles.LocalStyle + +@Composable +fun MachineStatus.toColor(): ColorPair { + val colors = LocalStyle.current.colors + return when (this) { + MachineStatus.Available -> ColorPair(colors.primary, colors.onPrimary) + MachineStatus.NotAvailable -> ColorPair(colors.error, colors.onError) + } +} + +@Composable +fun MachineStatus.toUnderColor(): ColorPair { + val colors = LocalStyle.current.colors + return when (this) { + MachineStatus.Available -> ColorPair(colors.primaryContainer, colors.onPrimaryContainer) + MachineStatus.NotAvailable -> ColorPair(colors.errorContainer, colors.onErrorContainer) + } +} + +fun MachineKind.toIcon(): String { + return when (this) { + MachineKind.Washer -> "washer.svg" + MachineKind.Dryer -> "fan.svg" + } +} + +@Composable +fun subbedColor(): ColorPair { + val colors = LocalStyle.current.colors + return ColorPair(colors.error, colors.onError) +} + +@Composable +fun unsubbedColor(): ColorPair { + val colors = LocalStyle.current.colors + return ColorPair(colors.primary, colors.onPrimary) +} \ No newline at end of file diff --git a/mosogepsch/src/jsMain/kotlin/localization/English.kt b/mosogepsch/src/jsMain/kotlin/localization/English.kt index 73d1aea..c5bb88a 100644 --- a/mosogepsch/src/jsMain/kotlin/localization/English.kt +++ b/mosogepsch/src/jsMain/kotlin/localization/English.kt @@ -17,6 +17,9 @@ abstract class Localization { open val lastUpdated = "Last updated" open val daysAgo = "days ago" + open val sub = "Subscribe" + open val unsub = "Unsubscribe" + protected open fun timeFormat(dateTime: LocalDateTime): String = "at ${dateTime.hour.padded()}:${dateTime.minute.padded()}" open fun floorFormat(floor: Int) = "on floor $floor" @@ -26,7 +29,6 @@ abstract class Localization { val dateTime = instant.toLocalDateTime(tz) val now = Clock.System.now() val diff = now - instant - println(diff.toString()) if (diff < Duration.Companion.days(1)) { return timeFormat(dateTime) } diff --git a/mosogepsch/src/jsMain/kotlin/localization/Hungarian.kt b/mosogepsch/src/jsMain/kotlin/localization/Hungarian.kt index 12b632d..f91015d 100644 --- a/mosogepsch/src/jsMain/kotlin/localization/Hungarian.kt +++ b/mosogepsch/src/jsMain/kotlin/localization/Hungarian.kt @@ -12,6 +12,9 @@ class Hungarian: Localization() { override val lastUpdated = "Utoljára frissítve" override val daysAgo = "napja" + override val sub = "Feliratkozás" + override val unsub = "Leiratkozás" + override fun timeFormat(dateTime: LocalDateTime): String = "${dateTime.hour.padded()}:${dateTime.minute.padded()}-kor" override fun floorFormat(floor: Int): String { val nevelo = when(floor) { diff --git a/mosogepsch/src/jsMain/kotlin/styles/FloorStyle.kt b/mosogepsch/src/jsMain/kotlin/styles/FloorStyle.kt new file mode 100644 index 0000000..58e6789 --- /dev/null +++ b/mosogepsch/src/jsMain/kotlin/styles/FloorStyle.kt @@ -0,0 +1,68 @@ +package styles + +import org.jetbrains.compose.web.css.* + +object FloorStyle: StyleSheet() { + val container by style { + display(DisplayStyle.Flex) + flexDirection(FlexDirection.Column) + justifyContent(JustifyContent.SpaceAround) + alignItems(AlignItems.Center) + position(Position.Relative) + } + + val card by style { + maxWidth(20.em) + width(20.em) + flexDirection(FlexDirection.Row) + justifyContent(JustifyContent.SpaceAround) + property("box-shadow", "0 0 .7em #00000033") + } + + val floorNum by style { + minWidth(2.75.ch) + marginBottom(0.px) + textAlign("right") + } + + val machine by style { + flexGrow(1) + cursor("pointer") + } + + val absCnt by style { + position(Position.Absolute) + left(0.px) + bottom(4.em) + width(0.px) + height(0.px) + property("z-index", -1) + } + val relCnt by style { + position(Position.Relative) + width(0.px) + height(0.px) + } + val underflowCnt by style { + margin(0.px) + padding(1.em) + paddingTop(0.em) + width(22.em) + overflow("hidden") + property("transition", "${Style.baseTransition}, max-height 0.75s") + } + val underflowCard by style { + flexDirection(FlexDirection.Column) + justifyContent(JustifyContent.FlexStart) + alignItems(AlignItems.Stretch) + padding(1.5.em) + margin(0.px) + width(100.percent) + paddingTop(5.em) + property("transition", "${Style.baseTransition}, box-shadow 0.5s") + property("box-shadow", "0 0 1em #00000044") + } + val subBtn by style { + cursor("pointer") + } +} \ No newline at end of file diff --git a/mosogepsch/src/jsMain/resources/index.html b/mosogepsch/src/jsMain/resources/index.html index 250a1dd..6b990b5 100644 --- a/mosogepsch/src/jsMain/resources/index.html +++ b/mosogepsch/src/jsMain/resources/index.html @@ -3,6 +3,7 @@ <head> <meta charset="UTF-8"> <title>MosógépSCH</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> </head> <body> -- GitLab