1
1
package com.microsoft.fluentui.tokenized.notification
2
2
3
+ import androidx.compose.animation.core.tween
3
4
import androidx.compose.foundation.background
4
5
import androidx.compose.foundation.clickable
6
+ import androidx.compose.foundation.gestures.detectHorizontalDragGestures
5
7
import androidx.compose.foundation.interaction.MutableInteractionSource
6
8
import androidx.compose.foundation.layout.*
7
9
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -14,6 +16,8 @@ import androidx.compose.ui.Alignment
14
16
import androidx.compose.ui.Modifier
15
17
import androidx.compose.ui.draw.clip
16
18
import androidx.compose.ui.graphics.graphicsLayer
19
+ import androidx.compose.ui.input.pointer.pointerInput
20
+ import androidx.compose.ui.platform.LocalConfiguration
17
21
import androidx.compose.ui.platform.testTag
18
22
import androidx.compose.ui.semantics.LiveRegionMode
19
23
import androidx.compose.ui.semantics.Role
@@ -27,6 +31,7 @@ import com.microsoft.fluentui.theme.token.Icon
27
31
import com.microsoft.fluentui.theme.token.StateColor
28
32
import com.microsoft.fluentui.theme.token.controlTokens.*
29
33
import com.microsoft.fluentui.tokenized.controls.Button
34
+ import com.microsoft.fluentui.util.dpToPx
30
35
import kotlinx.coroutines.CancellableContinuation
31
36
import kotlinx.coroutines.CoroutineScope
32
37
import kotlinx.coroutines.delay
@@ -154,6 +159,40 @@ class SnackbarState {
154
159
}
155
160
}
156
161
162
+ @Composable
163
+ fun Modifier.swipeToDismiss (
164
+ animationVariables : AnimationVariables ,
165
+ scope : CoroutineScope ,
166
+ metadata : SnackbarMetadata
167
+ ): Modifier {
168
+ val configuration = LocalConfiguration .current
169
+ val dismissThreshold =
170
+ dpToPx(configuration.screenWidthDp.dp) * 0.33f // One-third of screen width
171
+ return this .pointerInput(Unit ) {
172
+ detectHorizontalDragGestures(
173
+ onDragEnd = {
174
+ if (animationVariables.offsetX.value < - dismissThreshold) {
175
+ scope.launch {
176
+ metadata.dismiss()
177
+ }
178
+ } else {
179
+ scope.launch {
180
+ animationVariables.offsetX.animateTo(
181
+ 0f ,
182
+ animationSpec = tween(300 )
183
+ )
184
+ }
185
+ }
186
+ },
187
+ onHorizontalDrag = { _, dragAmount ->
188
+ scope.launch {
189
+ animationVariables.offsetX.snapTo(animationVariables.offsetX.value + dragAmount)
190
+ }
191
+ }
192
+ )
193
+ }
194
+ }
195
+
157
196
/* *
158
197
* Snackbar are transient Notification control used to deliver information which can be timedout or
159
198
* can be cleared by user pressing the CTA or dismiss icon. Snackbar is rendered using [SnackbarMetadata]
@@ -163,14 +202,16 @@ class SnackbarState {
163
202
* @param snackbarState Queue to store all the Notification requests.
164
203
* @param modifier Optional modifier to be applied to Snackbar.
165
204
* @param snackbarTokens Optional Tokens to redesign Snackbar.
205
+ * @param enableSwipeToDismiss Optional flag to enable swipe to dismiss functionality.
166
206
*/
167
207
@Composable
168
208
fun Snackbar (
169
209
snackbarState : SnackbarState ,
170
210
modifier : Modifier = Modifier ,
171
- snackbarTokens : SnackBarTokens ? = null
211
+ snackbarTokens : SnackBarTokens ? = null,
212
+ enableSwipeToDismiss : Boolean = false
172
213
) {
173
- val metadata: SnackbarMetadata = snackbarState.currentSnackbar ? : return
214
+ val metadata = snackbarState.currentSnackbar ? : return
174
215
val scope = rememberCoroutineScope()
175
216
176
217
val themeID =
@@ -195,8 +236,17 @@ fun Snackbar(
195
236
scope = scope,
196
237
animationBehavior = metadata.animationBehavior,
197
238
) { animationVariables ->
198
- Row (
239
+ val swipeToDismissModifier = if (enableSwipeToDismiss) {
240
+ modifier.swipeToDismiss(
241
+ animationVariables,
242
+ scope,
243
+ metadata
244
+ )
245
+ } else {
199
246
modifier
247
+ }
248
+ Row (
249
+ swipeToDismissModifier
200
250
.graphicsLayer(
201
251
scaleX = animationVariables.scale.value,
202
252
scaleY = animationVariables.scale.value,
0 commit comments