Skip to content

Commit e77063f

Browse files
committed
Merge branch 'dev'
2 parents de91636 + 390332d commit e77063f

File tree

14 files changed

+434
-40
lines changed

14 files changed

+434
-40
lines changed

composeApp/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ kotlin {
3333
commonMain.dependencies {
3434
implementation(compose.runtime)
3535
implementation(compose.foundation)
36-
implementation(compose.material3)
36+
implementation("org.jetbrains.compose.material3:material3:1.9.0-beta03")
37+
implementation("org.jetbrains.compose.material3:material3-window-size-class:1.9.0-beta03")
3738
implementation(compose.ui)
3839
implementation(compose.components.resources)
3940
implementation(compose.components.uiToolingPreview)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:fillColor="#e3e3e3"
8+
android:pathData="M160,760v-360q0,-19 8.5,-36t23.5,-28l240,-180q21,-16 48,-16t48,16l240,180q15,11 23.5,28t8.5,36v360q0,33 -23.5,56.5T720,840L600,840q-17,0 -28.5,-11.5T560,800v-200q0,-17 -11.5,-28.5T520,560h-80q-17,0 -28.5,11.5T400,600v200q0,17 -11.5,28.5T360,840L240,840q-33,0 -56.5,-23.5T160,760Z" />
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:fillColor="#e3e3e3"
8+
android:pathData="M480,680q17,0 28.5,-11.5T520,640v-160q0,-17 -11.5,-28.5T480,440q-17,0 -28.5,11.5T440,480v160q0,17 11.5,28.5T480,680ZM480,360q17,0 28.5,-11.5T520,320q0,-17 -11.5,-28.5T480,280q-17,0 -28.5,11.5T440,320q0,17 11.5,28.5T480,360ZM480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880Z" />
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:fillColor="#e3e3e3"
8+
android:pathData="M160,720q-17,0 -28.5,-11.5T120,680q0,-17 11.5,-28.5T160,640h640q17,0 28.5,11.5T840,680q0,17 -11.5,28.5T800,720L160,720ZM160,520q-17,0 -28.5,-11.5T120,480q0,-17 11.5,-28.5T160,440h640q17,0 28.5,11.5T840,480q0,17 -11.5,28.5T800,520L160,520ZM160,320q-17,0 -28.5,-11.5T120,280q0,-17 11.5,-28.5T160,240h640q17,0 28.5,11.5T840,280q0,17 -11.5,28.5T800,320L160,320Z" />
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:fillColor="#e3e3e3"
8+
android:pathData="M160,720q-17,0 -28.5,-11.5T120,680q0,-17 11.5,-28.5T160,640h440q17,0 28.5,11.5T640,680q0,17 -11.5,28.5T600,720L160,720ZM756,652L612,508q-12,-12 -12,-28t12,-28l144,-144q11,-11 28,-11t28,11q11,11 11,28t-11,28L696,480l116,116q11,11 11,28t-11,28q-11,11 -28,11t-28,-11ZM160,520q-17,0 -28.5,-11.5T120,480q0,-17 11.5,-28.5T160,440h320q17,0 28.5,11.5T520,480q0,17 -11.5,28.5T480,520L160,520ZM160,320q-17,0 -28.5,-11.5T120,280q0,-17 11.5,-28.5T160,240h440q17,0 28.5,11.5T640,280q0,17 -11.5,28.5T600,320L160,320Z" />
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:fillColor="#e3e3e3"
8+
android:pathData="M240,760h120v-200q0,-17 11.5,-28.5T400,520h160q17,0 28.5,11.5T600,560v200h120v-360L480,220 240,400v360ZM160,760v-360q0,-19 8.5,-36t23.5,-28l240,-180q21,-16 48,-16t48,16l240,180q15,11 23.5,28t8.5,36v360q0,33 -23.5,56.5T720,840L560,840q-17,0 -28.5,-11.5T520,800v-200h-80v200q0,17 -11.5,28.5T400,840L240,840q-33,0 -56.5,-23.5T160,760ZM480,490Z" />
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:fillColor="#e3e3e3"
8+
android:pathData="M480,680q17,0 28.5,-11.5T520,640v-160q0,-17 -11.5,-28.5T480,440q-17,0 -28.5,11.5T440,480v160q0,17 11.5,28.5T480,680ZM480,360q17,0 28.5,-11.5T520,320q0,-17 -11.5,-28.5T480,280q-17,0 -28.5,11.5T440,320q0,17 11.5,28.5T480,360ZM480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,800q134,0 227,-93t93,-227q0,-134 -93,-227t-227,-93q-134,0 -227,93t-93,227q0,134 93,227t227,93ZM480,480Z" />
9+
</vector>
Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,17 @@
11
package org.nsh07.nsh07
22

3-
import androidx.compose.foundation.layout.Arrangement
4-
import androidx.compose.foundation.layout.Box
5-
import androidx.compose.foundation.layout.Spacer
6-
import androidx.compose.foundation.layout.fillMaxHeight
73
import androidx.compose.foundation.layout.fillMaxSize
8-
import androidx.compose.foundation.layout.height
9-
import androidx.compose.foundation.lazy.LazyColumn
10-
import androidx.compose.material3.CircularWavyProgressIndicator
11-
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
12-
import androidx.compose.material3.Scaffold
13-
import androidx.compose.material3.Text
4+
import androidx.compose.material3.Surface
145
import androidx.compose.runtime.Composable
15-
import androidx.compose.ui.Alignment
166
import androidx.compose.ui.Modifier
17-
import androidx.compose.ui.text.style.TextAlign
18-
import androidx.compose.ui.unit.dp
19-
import androidx.compose.ui.unit.sp
7+
import org.nsh07.nsh07.ui.AppScreen
208
import org.nsh07.nsh07.ui.theme.Nsh07Theme
219

22-
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
2310
@Composable
2411
fun App() {
2512
Nsh07Theme {
26-
Scaffold {
27-
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
28-
LazyColumn(
29-
horizontalAlignment = Alignment.CenterHorizontally,
30-
verticalArrangement = Arrangement.Center,
31-
modifier = Modifier.fillMaxHeight()
32-
) {
33-
item {
34-
CircularWavyProgressIndicator()
35-
Spacer(Modifier.height(16.dp))
36-
Text(
37-
"Coming Soon...",
38-
fontSize = 64.sp,
39-
textAlign = TextAlign.Center,
40-
lineHeight = 68.sp
41-
)
42-
}
43-
}
44-
}
13+
Surface {
14+
AppScreen(modifier = Modifier.fillMaxSize())
4515
}
4616
}
4717
}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package org.nsh07.nsh07.ui
2+
3+
import androidx.compose.animation.AnimatedContent
4+
import androidx.compose.animation.Crossfade
5+
import androidx.compose.animation.core.animateFloatAsState
6+
import androidx.compose.animation.fadeIn
7+
import androidx.compose.animation.fadeOut
8+
import androidx.compose.animation.togetherWith
9+
import androidx.compose.foundation.layout.Row
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.material3.Icon
13+
import androidx.compose.material3.IconButton
14+
import androidx.compose.material3.MaterialTheme.typography
15+
import androidx.compose.material3.ModalWideNavigationRail
16+
import androidx.compose.material3.Text
17+
import androidx.compose.material3.WideNavigationRail
18+
import androidx.compose.material3.WideNavigationRailItem
19+
import androidx.compose.material3.WideNavigationRailValue
20+
import androidx.compose.material3.rememberWideNavigationRailState
21+
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
22+
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
23+
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
24+
import androidx.compose.runtime.Composable
25+
import androidx.compose.runtime.LaunchedEffect
26+
import androidx.compose.runtime.getValue
27+
import androidx.compose.runtime.mutableStateOf
28+
import androidx.compose.runtime.remember
29+
import androidx.compose.runtime.rememberCoroutineScope
30+
import androidx.compose.runtime.setValue
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.draw.rotate
33+
import androidx.compose.ui.semantics.semantics
34+
import androidx.compose.ui.semantics.stateDescription
35+
import androidx.compose.ui.unit.dp
36+
import androidx.compose.ui.util.fastForEach
37+
import kotlinx.coroutines.launch
38+
import nsh07.composeapp.generated.resources.Res
39+
import nsh07.composeapp.generated.resources.filled_home
40+
import nsh07.composeapp.generated.resources.filled_info
41+
import nsh07.composeapp.generated.resources.menu
42+
import nsh07.composeapp.generated.resources.menu_open
43+
import nsh07.composeapp.generated.resources.outline_home
44+
import nsh07.composeapp.generated.resources.outline_info
45+
import org.jetbrains.compose.resources.painterResource
46+
import org.nsh07.nsh07.ui.about.AboutScreen
47+
import org.nsh07.nsh07.ui.home.HomeScreen
48+
49+
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
50+
@Composable
51+
fun AppScreen(modifier: Modifier = Modifier) {
52+
var currentPortfolioScreen by remember { mutableStateOf(PortfolioScreen.HOME) }
53+
54+
val windowSizeClass = calculateWindowSizeClass()
55+
val state = rememberWideNavigationRailState(WideNavigationRailValue.Collapsed)
56+
val scope = rememberCoroutineScope()
57+
58+
val railItems = remember {
59+
listOf(
60+
RailItem(
61+
"Home",
62+
Res.drawable.outline_home,
63+
Res.drawable.filled_home,
64+
PortfolioScreen.HOME
65+
),
66+
RailItem(
67+
"About me",
68+
Res.drawable.outline_info,
69+
Res.drawable.filled_info,
70+
PortfolioScreen.ABOUT
71+
),
72+
)
73+
}
74+
75+
Row(modifier.fillMaxWidth()) {
76+
when (windowSizeClass.widthSizeClass) {
77+
WindowWidthSizeClass.Expanded -> {
78+
LaunchedEffect(Unit) {
79+
state.expand()
80+
}
81+
WideNavigationRail(
82+
state = state
83+
) {
84+
railItems.fastForEach {
85+
val selected = it.portfolioScreen == currentPortfolioScreen
86+
WideNavigationRailItem(
87+
selected = selected,
88+
onClick = { currentPortfolioScreen = it.portfolioScreen },
89+
icon = {
90+
Crossfade(selected) { targetState ->
91+
when (targetState) {
92+
true -> Icon(painterResource(it.selectedIcon), null)
93+
else -> Icon(painterResource(it.unselectedIcon), null)
94+
}
95+
}
96+
},
97+
label = { Text(it.name, style = typography.labelLarge) },
98+
railExpanded = state.targetValue == WideNavigationRailValue.Expanded
99+
)
100+
}
101+
}
102+
}
103+
104+
WindowWidthSizeClass.Medium -> {
105+
WideNavigationRail(
106+
state = state,
107+
header = {
108+
val rotation by animateFloatAsState(if (state.targetValue == WideNavigationRailValue.Expanded) 0f else 180f)
109+
IconButton(
110+
modifier =
111+
Modifier
112+
.padding(start = 24.dp)
113+
.rotate(rotation)
114+
.semantics {
115+
stateDescription =
116+
if (state.currentValue == WideNavigationRailValue.Expanded) {
117+
"Expanded"
118+
} else {
119+
"Collapsed"
120+
}
121+
},
122+
onClick = {
123+
scope.launch {
124+
if (state.targetValue == WideNavigationRailValue.Expanded)
125+
state.collapse()
126+
else state.expand()
127+
}
128+
},
129+
) {
130+
Crossfade(state.targetValue) {
131+
when (it) {
132+
WideNavigationRailValue.Expanded -> Icon(
133+
painterResource(Res.drawable.menu_open),
134+
"Collapse rail"
135+
)
136+
137+
WideNavigationRailValue.Collapsed -> Icon(
138+
painterResource(Res.drawable.menu),
139+
"Expand rail"
140+
)
141+
}
142+
}
143+
}
144+
}
145+
) {
146+
railItems.fastForEach {
147+
val selected = it.portfolioScreen == currentPortfolioScreen
148+
WideNavigationRailItem(
149+
selected = selected,
150+
onClick = {
151+
scope.launch {
152+
currentPortfolioScreen = it.portfolioScreen
153+
state.collapse()
154+
}
155+
},
156+
icon = {
157+
Crossfade(selected) { targetState ->
158+
when (targetState) {
159+
true -> Icon(painterResource(it.selectedIcon), null)
160+
else -> Icon(painterResource(it.unselectedIcon), null)
161+
}
162+
}
163+
},
164+
label = { Text(it.name, style = typography.labelLarge) },
165+
railExpanded = state.targetValue == WideNavigationRailValue.Expanded
166+
)
167+
}
168+
}
169+
}
170+
171+
WindowWidthSizeClass.Compact -> {
172+
ModalWideNavigationRail(
173+
state = state,
174+
hideOnCollapse = true
175+
) {
176+
railItems.fastForEach {
177+
val selected = it.portfolioScreen == currentPortfolioScreen
178+
WideNavigationRailItem(
179+
selected = selected,
180+
onClick = {
181+
scope.launch {
182+
currentPortfolioScreen = it.portfolioScreen
183+
state.collapse()
184+
}
185+
},
186+
icon = {
187+
Crossfade(selected) { targetState ->
188+
when (targetState) {
189+
true -> Icon(painterResource(it.selectedIcon), null)
190+
else -> Icon(painterResource(it.unselectedIcon), null)
191+
}
192+
}
193+
},
194+
label = { Text(it.name, style = typography.labelLarge) },
195+
railExpanded = state.targetValue == WideNavigationRailValue.Expanded
196+
)
197+
}
198+
}
199+
}
200+
}
201+
202+
AnimatedContent(
203+
currentPortfolioScreen,
204+
transitionSpec = { fadeIn().togetherWith(fadeOut()) }
205+
) {
206+
when (it) {
207+
PortfolioScreen.HOME -> HomeScreen(
208+
expandRail = { scope.launch { state.expand() } },
209+
windowWidthClass = windowSizeClass.widthSizeClass
210+
)
211+
212+
PortfolioScreen.ABOUT -> AboutScreen(
213+
expandRail = { scope.launch { state.expand() } },
214+
windowWidthClass = windowSizeClass.widthSizeClass
215+
)
216+
217+
PortfolioScreen.PROJECTS -> {}
218+
}
219+
}
220+
}
221+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.nsh07.nsh07.ui
2+
3+
import org.jetbrains.compose.resources.DrawableResource
4+
5+
enum class PortfolioScreen {
6+
HOME, ABOUT, PROJECTS
7+
}
8+
9+
data class RailItem(
10+
val name: String,
11+
val unselectedIcon: DrawableResource,
12+
val selectedIcon: DrawableResource,
13+
val portfolioScreen: PortfolioScreen
14+
)

0 commit comments

Comments
 (0)