11package com.mofeejegi.alert.ui.composable
22
33import androidx.compose.animation.AnimatedVisibility
4- import androidx.compose.animation.animateContentSize
54import androidx.compose.animation.core.EaseInOut
5+ import androidx.compose.animation.core.FiniteAnimationSpec
66import androidx.compose.animation.core.Spring
77import androidx.compose.animation.core.spring
88import androidx.compose.animation.core.tween
9+ import androidx.compose.animation.expandVertically
910import androidx.compose.animation.fadeOut
1011import androidx.compose.animation.scaleOut
12+ import androidx.compose.animation.shrinkVertically
1113import androidx.compose.animation.slideInVertically
1214import androidx.compose.foundation.ExperimentalFoundationApi
1315import androidx.compose.foundation.background
@@ -20,7 +22,6 @@ import androidx.compose.foundation.layout.systemBarsPadding
2022import androidx.compose.foundation.layout.widthIn
2123import androidx.compose.foundation.layout.wrapContentHeight
2224import androidx.compose.foundation.lazy.LazyColumn
23- import androidx.compose.foundation.lazy.LazyItemScope
2425import androidx.compose.foundation.lazy.items
2526import androidx.compose.foundation.shape.RoundedCornerShape
2627import androidx.compose.material.Icon
@@ -30,24 +31,29 @@ import androidx.compose.runtime.Composable
3031import androidx.compose.runtime.DisposableEffect
3132import androidx.compose.runtime.LaunchedEffect
3233import androidx.compose.runtime.collectAsState
34+ import androidx.compose.runtime.derivedStateOf
3335import androidx.compose.runtime.getValue
3436import androidx.compose.ui.Alignment
3537import androidx.compose.ui.Modifier
3638import androidx.compose.ui.graphics.Color
3739import androidx.compose.ui.text.TextStyle
3840import androidx.compose.ui.text.style.TextOverflow
3941import androidx.compose.ui.unit.dp
42+ import androidx.compose.ui.window.Popup
43+ import androidx.compose.ui.window.PopupProperties
4044import com.mofeejegi.alert.alert_banner.generated.resources.Res
4145import com.mofeejegi.alert.alert_banner.generated.resources.ic_close
46+ import com.mofeejegi.alert.ui.bannertype.AlertBannerType
4247import com.mofeejegi.alert.ui.state.AlertAnimatedIn
4348import com.mofeejegi.alert.ui.state.AlertAnimatedOut
4449import com.mofeejegi.alert.ui.state.AlertBannerState
45- import com.mofeejegi.alert.ui.bannertype.AlertBannerType
4650import com.mofeejegi.alert.ui.state.AlertBannerViewEvent
4751import com.mofeejegi.alert.ui.state.AlertBannerViewModel
4852import com.mofeejegi.alert.ui.state.AlertDismissed
53+ import com.mofeejegi.alert.ui.theme.AlertTheme
4954import kotlinx.coroutines.delay
5055import org.jetbrains.compose.resources.painterResource
56+ import org.jetbrains.compose.ui.tooling.preview.Preview
5157
5258@Composable
5359internal fun AlertBannerView (
@@ -56,32 +62,43 @@ internal fun AlertBannerView(
5662 onAlertColor : Color ,
5763) {
5864 val viewState by vm.viewState.collectAsState()
65+ val alertsToDisplay by derivedStateOf { viewState.orderedAlerts() }
5966
60- LazyColumn (
61- Modifier
62- .systemBarsPadding()
63- .fillMaxWidth()
64- .wrapContentHeight()
65- .animateContentSize(),
66- userScrollEnabled = false ,
67- ) {
68- items(
69- items = viewState.orderedAlerts(),
70- key = { alertState -> alertState.id },
71- ) {
72- AlertBannerWrapper (
73- alertState = it,
74- textStyle = textStyle,
75- onAlertColor = onAlertColor,
76- eventProcessor = vm::processEvent,
67+ if (alertsToDisplay.isNotEmpty()) {
68+ Popup (
69+ alignment = Alignment .TopCenter ,
70+ properties = PopupProperties (
71+ focusable = false ,
72+ dismissOnBackPress = false ,
73+ dismissOnClickOutside = false ,
7774 )
75+ ) {
76+ LazyColumn (
77+ Modifier
78+ .systemBarsPadding()
79+ .fillMaxWidth()
80+ .wrapContentHeight(),
81+ userScrollEnabled = false ,
82+ ) {
83+ items(
84+ items = alertsToDisplay,
85+ key = { alertState -> alertState.id },
86+ ) {
87+ AlertBannerWrapper (
88+ alertState = it,
89+ textStyle = textStyle,
90+ onAlertColor = onAlertColor,
91+ eventProcessor = vm::processEvent,
92+ )
93+ }
94+ }
7895 }
7996 }
8097}
8198
8299@OptIn(ExperimentalFoundationApi ::class )
83100@Composable
84- private fun LazyItemScope. AlertBannerWrapper (
101+ private fun AlertBannerWrapper (
85102 alertState : AlertBannerState ,
86103 textStyle : TextStyle ,
87104 onAlertColor : Color ,
@@ -101,17 +118,18 @@ private fun LazyItemScope.AlertBannerWrapper(
101118 }
102119 }
103120
121+ fun <T > animationSpec (): FiniteAnimationSpec <T > = spring(
122+ dampingRatio = Spring .DampingRatioNoBouncy ,
123+ stiffness = Spring .StiffnessLow ,
124+ )
125+
104126 AnimatedVisibility (
105- modifier = Modifier .animateItemPlacement(),
106127 visible = alertState.visible,
107- enter = slideInVertically(
108- animationSpec = spring(
109- dampingRatio = Spring .DampingRatioNoBouncy ,
110- stiffness = Spring .StiffnessLow ,
111- ),
112- ) { - it },
113- exit = scaleOut(animationSpec = tween(easing = EaseInOut ))
114- + fadeOut(animationSpec = tween(easing = EaseInOut )),
128+ enter = expandVertically(animationSpec = animationSpec())
129+ + slideInVertically(animationSpec = animationSpec()) { - it },
130+ exit = shrinkVertically(shrinkTowards = Alignment .CenterVertically , animationSpec = animationSpec())
131+ + fadeOut(animationSpec = tween(durationMillis = 200 , easing = EaseInOut ))
132+ + scaleOut(animationSpec = tween(easing = EaseInOut )),
115133 ) {
116134 AlertBanner (
117135 modifier = Modifier .padding(vertical = 8 .dp, horizontal = 16 .dp),
@@ -184,3 +202,47 @@ private fun AlertBanner(
184202 }
185203 }
186204}
205+
206+ @Composable
207+ @Preview
208+ fun PreviewSuccessBanner () {
209+ AlertTheme (darkTheme = false ) {
210+ Box (
211+ modifier = Modifier
212+ .background(Color .White )
213+ .size(400 .dp)
214+ ) {
215+ AlertBanner (
216+ id = " success-banner" ,
217+ message = " Success message" ,
218+ type = AlertBannerType .Success ,
219+ eventProcessor = {},
220+ textStyle = TextStyle .Default ,
221+ onAlertColor = Color .White ,
222+ onDismiss = {},
223+ )
224+ }
225+ }
226+ }
227+
228+ @Composable
229+ @Preview
230+ fun PreviewErrorBanner () {
231+ AlertTheme (darkTheme = false ) {
232+ Box (
233+ modifier = Modifier
234+ .background(Color .White )
235+ .size(400 .dp)
236+ ) {
237+ AlertBanner (
238+ id = " error-banner" ,
239+ message = " Error message" ,
240+ type = AlertBannerType .Error ,
241+ eventProcessor = {},
242+ textStyle = TextStyle .Default ,
243+ onAlertColor = Color .White ,
244+ onDismiss = {},
245+ )
246+ }
247+ }
248+ }
0 commit comments