refactor(ui): split into shared, maps, and main modules

This commit is contained in:
Cilly Leang 2026-03-26 02:55:46 +11:00
parent aab03ced07
commit a79c95829e
Signed by: cilly
GPG key ID: 6500251E087653C9
43 changed files with 539 additions and 378 deletions

View file

@ -0,0 +1,50 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
alias(libs.plugins.androidMultiplatformLibrary)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
}
kotlin {
android {
namespace = "moe.lava.banksia.ui.shared"
compileSdk = libs.versions.android.compileSdk.get().toInt()
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
compilerOptions {
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
freeCompilerArgs.add("-Xexplicit-backing-fields")
}
iosArm64()
iosSimulatorArm64()
sourceSets {
commonMain.dependencies {
implementation(libs.compose.components.resources)
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.material3)
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
implementation(projects.shared)
}
}
}
dependencies {
androidRuntimeClasspath(libs.compose.ui.tooling)
}
compose.resources {
publicResClass = true
packageOfResClass = "moe.lava.banksia.resources"
}

View file

@ -0,0 +1,24 @@
package moe.lava.banksia.ui.platform
import android.os.Build
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
@Composable
actual fun BanksiaTheme.colors(
darkTheme: Boolean,
dynamicColor: Boolean
): ColorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> darkColorScheme()
else -> lightColorScheme()
}

View file

@ -0,0 +1,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:gravity="fill"
android:drawable="@drawable/bus_background"
/>
<item
android:gravity="center"
android:drawable="@drawable/bus_icon"
android:top="5dp"
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
/>
</layer-list>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF8200"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M4,16c0,0.88 0.39,1.67 1,2.22L5,20c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h8v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1.78c0.61,-0.55 1,-1.34 1,-2.22L20,6c0,-3.5 -3.58,-4 -8,-4s-8,0.5 -8,4v10zM7.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zM16.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM18,11L6,11L6,6h12v5z"/>
</vector>

View file

@ -0,0 +1,36 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="600dp"
android:height="600dp"
android:viewportWidth="600"
android:viewportHeight="600">
<path
android:pathData="M301.21,418.53C300.97,418.54 300.73,418.56 300.49,418.56C297.09,418.59 293.74,417.72 290.79,416.05L222.6,377.54C220.63,376.43 219,374.82 217.85,372.88C216.7,370.94 216.09,368.73 216.07,366.47L216.07,288.16C216.06,287.32 216.09,286.49 216.17,285.67C216.38,283.54 216.91,281.5 217.71,279.6L199.29,268.27L177.74,256.19C175.72,260.43 174.73,265.23 174.78,270.22L174.79,387.05C174.85,393.89 178.57,400.2 184.53,403.56L286.26,461.02C290.67,463.51 295.66,464.8 300.73,464.76C300.91,464.76 301.09,464.74 301.27,464.74C301.24,449.84 301.22,439.23 301.22,439.23L301.21,418.53Z"
android:fillColor="#041619"
android:fillType="nonZero"/>
<path
android:pathData="M409.45,242.91L312.64,188.23C303.64,183.15 292.58,183.26 283.68,188.51L187.92,245C183.31,247.73 179.93,251.62 177.75,256.17L177.74,256.19L199.29,268.27L217.71,279.6C217.83,279.32 217.92,279.02 218.05,278.74C218.24,278.36 218.43,277.98 218.64,277.62C219.06,276.88 219.52,276.18 220.04,275.51C221.37,273.8 223.01,272.35 224.87,271.25L289.06,233.39C290.42,232.59 291.87,231.96 293.39,231.51C295.53,230.87 297.77,230.6 300,230.72C302.98,230.88 305.88,231.73 308.47,233.2L373.37,269.85C375.54,271.08 377.49,272.68 379.13,274.57C379.68,275.19 380.18,275.85 380.65,276.53C380.86,276.84 381.05,277.15 381.24,277.47L397.79,266.39L420.34,252.93L420.31,252.88C417.55,248.8 413.77,245.35 409.45,242.91Z"
android:fillColor="#37BF6E"
android:fillType="nonZero"/>
<path
android:pathData="M381.24,277.47C381.51,277.92 381.77,278.38 382.01,278.84C382.21,279.24 382.39,279.65 382.57,280.06C382.91,280.88 383.19,281.73 383.41,282.59C383.74,283.88 383.92,285.21 383.93,286.57L383.93,361.1C383.96,363.95 383.35,366.77 382.16,369.36C381.93,369.86 381.69,370.35 381.42,370.83C379.75,373.79 377.32,376.27 374.39,378L310.2,415.87C307.47,417.48 304.38,418.39 301.21,418.53L301.22,439.23C301.22,439.23 301.24,449.84 301.27,464.74C306.1,464.61 310.91,463.3 315.21,460.75L410.98,404.25C419.88,399 425.31,389.37 425.22,379.03L425.22,267.85C425.17,262.48 423.34,257.34 420.34,252.93L397.79,266.39L381.24,277.47Z"
android:fillColor="#3870B2"
android:fillType="nonZero"/>
<path
android:pathData="M177.75,256.17C179.93,251.62 183.31,247.73 187.92,245L283.68,188.51C292.58,183.26 303.64,183.15 312.64,188.23L409.45,242.91C413.77,245.35 417.55,248.8 420.31,252.88L420.34,252.93L498.59,206.19C494.03,199.46 487.79,193.78 480.67,189.75L320.86,99.49C306.01,91.1 287.75,91.27 273.07,99.95L114.99,193.2C107.39,197.69 101.81,204.11 98.21,211.63L177.74,256.19L177.75,256.17ZM301.27,464.74C301.09,464.74 300.91,464.76 300.73,464.76C295.66,464.8 290.67,463.51 286.26,461.02L184.53,403.56C178.57,400.2 174.85,393.89 174.79,387.05L174.78,270.22C174.73,265.23 175.72,260.43 177.74,256.19L98.21,211.63C94.86,218.63 93.23,226.58 93.31,234.82L93.31,427.67C93.42,438.97 99.54,449.37 109.4,454.92L277.31,549.77C284.6,553.88 292.84,556.01 301.2,555.94L301.2,555.8C301.39,543.78 301.33,495.26 301.27,464.74Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M498.59,206.19L420.34,252.93C423.34,257.34 425.17,262.48 425.22,267.85L425.22,379.03C425.31,389.37 419.88,399 410.98,404.25L315.21,460.75C310.91,463.3 306.1,464.61 301.27,464.74C301.33,495.26 301.39,543.78 301.2,555.8L301.2,555.94C309.48,555.87 317.74,553.68 325.11,549.32L483.18,456.06C497.87,447.39 506.85,431.49 506.69,414.43L506.69,230.91C506.6,222.02 503.57,213.5 498.59,206.19Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M301.2,555.94C292.84,556.01 284.6,553.88 277.31,549.76L109.4,454.92C99.54,449.37 93.42,438.97 93.31,427.67L93.31,234.82C93.23,226.58 94.86,218.63 98.21,211.63C101.81,204.11 107.39,197.69 114.99,193.2L273.07,99.95C287.75,91.27 306.01,91.1 320.86,99.49L480.67,189.75C487.79,193.78 494.03,199.46 498.59,206.19C503.57,213.5 506.6,222.02 506.69,230.91L506.69,414.43C506.85,431.49 497.87,447.39 483.18,456.06L325.11,549.32C317.74,553.68 309.48,555.87 301.2,555.94Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
</vector>

View file

@ -0,0 +1,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:gravity="fill"
android:drawable="@drawable/train_background"
/>
<item
android:gravity="center"
android:drawable="@drawable/train_icon"
android:top="5dp"
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
/>
</layer-list>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#0072CE"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M12,2c-4,0 -8,0.5 -8,4v9.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h2.23l2,-2L14,19l2,2h2v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5L20,6c0,-3.5 -3.58,-4 -8,-4zM7.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zM11,10L6,10L6,6h5v4zM13,10L13,6h5v4h-5zM16.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>

View file

@ -0,0 +1,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:gravity="fill"
android:drawable="@drawable/tram_background"
/>
<item
android:gravity="center"
android:drawable="@drawable/tram_icon"
android:top="5dp"
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
/>
</layer-list>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#78BE20"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M19,16.94L19,8.5c0,-2.79 -2.61,-3.4 -6.01,-3.49l0.76,-1.51L17,3.5L17,2L7,2v1.5h4.75l-0.76,1.52C7.86,5.11 5,5.73 5,8.5v8.44c0,1.45 1.19,2.66 2.59,2.97L6,21.5v0.5h2.23l2,-2L14,20l2,2h2v-0.5L16.5,20h-0.08c1.69,0 2.58,-1.37 2.58,-3.06zM12,18.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM17,14L7,14L7,9h10v5z"/>
</vector>

View file

@ -0,0 +1,52 @@
package moe.lava.banksia.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import moe.lava.banksia.model.RouteType
import moe.lava.banksia.model.RouteType.MetroBus
import moe.lava.banksia.model.RouteType.MetroTrain
import moe.lava.banksia.model.RouteType.MetroTram
import moe.lava.banksia.ui.extensions.getUIProperties
import org.jetbrains.compose.resources.painterResource
@Composable
fun RouteIcon(
modifier: Modifier = Modifier.Companion,
size: Dp = 40.dp,
routeType: RouteType,
) {
val properties = routeType.getUIProperties()
Image(
painter = painterResource(properties.icon),
contentDescription = null,
modifier = modifier
.size(size)
.aspectRatio(1f)
.padding(size * ICON_PADDING / 2)
.drawBehind {
drawCircle(properties.colour, radius = size.toPx() / 2f)
}
)
}
const val ICON_PADDING = 0.25f
@Preview
@Composable
internal fun RouteIconPreview() {
Row {
RouteIcon(routeType = MetroTrain)
RouteIcon(routeType = MetroTram)
RouteIcon(routeType = MetroBus)
}
}

View file

@ -0,0 +1,84 @@
package moe.lava.banksia.ui.extensions
import androidx.compose.ui.graphics.Color
import moe.lava.banksia.data.ptv.structures.PtvRouteType
import moe.lava.banksia.model.RouteType
import moe.lava.banksia.resources.Res
import moe.lava.banksia.resources.bus
import moe.lava.banksia.resources.bus_background
import moe.lava.banksia.resources.bus_icon
import moe.lava.banksia.resources.train
import moe.lava.banksia.resources.train_background
import moe.lava.banksia.resources.train_icon
import moe.lava.banksia.resources.tram
import moe.lava.banksia.resources.tram_background
import moe.lava.banksia.resources.tram_icon
import org.jetbrains.compose.resources.DrawableResource
data class RouteTypeProperties(
val colour: Color,
val drawable: DrawableResource,
val background: DrawableResource,
val icon: DrawableResource,
)
const val TRAIN_BLUE = 0xFF0072CE
const val TRAM_GREEN = 0xFF78BE20
const val BUS_ORANGE = 0xFFFF8200
const val VLINE_PURPLE = 0xFF8F1A95
fun RouteType.getUIProperties(): RouteTypeProperties {
val colour = when (this) {
RouteType.MetroTrain -> TRAIN_BLUE
RouteType.MetroTram -> TRAM_GREEN
RouteType.MetroBus -> BUS_ORANGE
RouteType.RegionalTrain -> VLINE_PURPLE
RouteType.RegionalCoach -> VLINE_PURPLE
RouteType.RegionalBus -> VLINE_PURPLE
RouteType.SkyBus -> BUS_ORANGE
RouteType.Interstate -> BUS_ORANGE
}
val (drawable, background, icon) = when (this) {
RouteType.MetroTrain,
RouteType.RegionalTrain,
RouteType.Interstate -> Triple(
Res.drawable.train, Res.drawable.train_background, Res.drawable.train_icon
)
RouteType.MetroTram -> Triple(
Res.drawable.tram, Res.drawable.tram_background, Res.drawable.tram_icon
)
RouteType.MetroBus,
RouteType.RegionalCoach,
RouteType.RegionalBus,
RouteType.SkyBus -> Triple(
Res.drawable.bus, Res.drawable.bus_background, Res.drawable.bus_icon
)
}
return RouteTypeProperties(Color(colour), drawable, background, icon)
}
fun PtvRouteType.getUIProperties(): RouteTypeProperties {
val colour = when (this) {
PtvRouteType.TRAIN -> Color(TRAIN_BLUE)
PtvRouteType.TRAM -> Color(TRAM_GREEN)
PtvRouteType.BUS, PtvRouteType.NIGHT_BUS -> Color(BUS_ORANGE)
PtvRouteType.VLINE -> Color(VLINE_PURPLE)
}
val (drawable, background, icon) = when (this) {
PtvRouteType.TRAM -> Triple(
Res.drawable.tram, Res.drawable.tram_background, Res.drawable.tram_icon
)
PtvRouteType.TRAIN, PtvRouteType.VLINE -> Triple(
Res.drawable.train, Res.drawable.train_background, Res.drawable.train_icon
)
PtvRouteType.BUS, PtvRouteType.NIGHT_BUS -> Triple(
Res.drawable.bus, Res.drawable.bus_background, Res.drawable.bus_icon
)
}
return RouteTypeProperties(colour, drawable, background, icon)
}

View file

@ -0,0 +1,29 @@
package moe.lava.banksia.ui.platform
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.runtime.Composable
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun BanksiaTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable (() -> Unit)
) {
MaterialExpressiveTheme(
colorScheme = BanksiaTheme.colors(darkTheme, dynamicColor),
content = content,
)
}
@Composable
expect fun BanksiaTheme.colors(darkTheme: Boolean, dynamicColor: Boolean): ColorScheme
object BanksiaTheme {
val colors: ColorScheme
@Composable
get() = colors(isSystemInDarkTheme(), true)
}

View file

@ -0,0 +1,15 @@
package moe.lava.banksia.ui.platform
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
@Composable
actual fun BanksiaTheme.colors(
darkTheme: Boolean,
dynamicColor: Boolean
): ColorScheme = when {
darkTheme -> darkColorScheme()
else -> lightColorScheme()
}