@momo-kits/native-kits 0.156.6-dialog.1-debug → 0.156.6-newcompose.1-debug

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/compose/build.gradle.kts +1 -4
  2. package/compose/build.gradle.kts.backup +0 -3
  3. package/compose/compose.podspec +11 -5
  4. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -162
  5. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +12 -0
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +19 -17
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +198 -0
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +42 -51
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +23 -10
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +15 -12
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +14 -13
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +6 -7
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +576 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -4
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +1 -1
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +1 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +1 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +1 -49
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +152 -48
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +14 -1
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +29 -12
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +4 -2
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +2 -1
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +4 -1
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +1 -22
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  27. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +2 -178
  28. package/gradle/libs.versions.toml +1 -1
  29. package/gradle.properties +1 -1
  30. package/ios/Application/FloatingButton.swift +1 -1
  31. package/ios/Badge/BadgeRibbon.swift +2 -2
  32. package/ios/Image/Image.swift +5 -1
  33. package/ios/Input/Input.swift +2 -2
  34. package/ios/Input/InputPhoneNumber.swift +7 -3
  35. package/ios/Popup/PopupDisplay.swift +6 -12
  36. package/local.properties +1 -1
  37. package/package.json +1 -1
  38. package/publish.sh +26 -1
  39. package/compose/MoMoComposeKits.podspec +0 -54
@@ -40,7 +40,7 @@ kotlin {
40
40
  }
41
41
 
42
42
  cocoapods {
43
- version = "0.156.6-dialog.1-debug"
43
+ version = "0.156.6-newcompose.1-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -54,9 +54,6 @@ kotlin {
54
54
  moduleName = "Lottie"
55
55
  version = "4.4.3"
56
56
  extraOpts += listOf("-compiler-option", "-fmodules")
57
- }
58
- pod("SDWebImage") {
59
- version = ">= 5.19.1"
60
57
  }
61
58
  }
62
59
 
@@ -54,9 +54,6 @@ kotlin {
54
54
  moduleName = "Lottie"
55
55
  version = "4.4.3"
56
56
  extraOpts += listOf("-compiler-option", "-fmodules")
57
- }
58
- pod("SDWebImage") {
59
- version = ">= 5.19.1"
60
57
  }
61
58
  }
62
59
 
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.156.1-test.6'
3
+ spec.version = '0.156.6-beta.16'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
@@ -9,22 +9,28 @@ Pod::Spec.new do |spec|
9
9
  spec.vendored_frameworks = 'build/cocoapods/framework/kits.framework'
10
10
  spec.libraries = 'c++'
11
11
  spec.ios.deployment_target = '15.0'
12
- spec.dependency 'SDWebImage', '>= 5.19.1'
13
12
  spec.dependency 'lottie-ios', '4.4.3'
13
+
14
14
  if !Dir.exist?('build/cocoapods/framework/kits.framework') || Dir.empty?('build/cocoapods/framework/kits.framework')
15
15
  raise "
16
+
16
17
  Kotlin framework 'kits' doesn't exist yet, so a proper Xcode project can't be generated.
17
18
  'pod install' should be executed after running ':generateDummyFramework' Gradle task:
19
+
18
20
  ./gradlew :compose:generateDummyFramework
21
+
19
22
  Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)"
20
23
  end
24
+
21
25
  spec.xcconfig = {
22
26
  'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO',
23
27
  }
28
+
24
29
  spec.pod_target_xcconfig = {
25
30
  'KOTLIN_PROJECT_PATH' => ':compose',
26
31
  'PRODUCT_MODULE_NAME' => 'kits',
27
32
  }
33
+
28
34
  spec.script_phases = [
29
35
  {
30
36
  :name => 'Build compose',
@@ -32,8 +38,8 @@ Pod::Spec.new do |spec|
32
38
  :shell_path => '/bin/sh',
33
39
  :script => <<-SCRIPT
34
40
  if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then
35
- echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\""
36
- exit 0
41
+ echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\""
42
+ exit 0
37
43
  fi
38
44
  set -ev
39
45
  REPO_ROOT="$PODS_TARGET_SRCROOT"
@@ -45,4 +51,4 @@ Pod::Spec.new do |spec|
45
51
  }
46
52
  ]
47
53
  spec.resources = ['build/compose/cocoapods/compose-resources']
48
- end
54
+ end
@@ -9,7 +9,6 @@ import android.os.Build
9
9
  import androidx.compose.foundation.layout.Box
10
10
  import androidx.compose.runtime.Composable
11
11
  import androidx.compose.runtime.getValue
12
- import androidx.compose.runtime.rememberUpdatedState
13
12
  import androidx.compose.ui.Modifier
14
13
  import androidx.compose.ui.graphics.Color
15
14
  import androidx.compose.ui.graphics.NativePaint
@@ -24,23 +23,6 @@ import com.airbnb.lottie.compose.LottieConstants
24
23
  import com.airbnb.lottie.compose.rememberLottieComposition
25
24
  import com.airbnb.lottie.compose.rememberLottieDynamicProperties
26
25
  import com.airbnb.lottie.compose.rememberLottieDynamicProperty
27
- import android.graphics.Matrix
28
- import android.widget.ImageView
29
- import androidx.compose.foundation.layout.fillMaxSize
30
- import androidx.compose.ui.Alignment
31
- import androidx.compose.ui.layout.ContentScale
32
- import androidx.compose.ui.viewinterop.AndroidView
33
- import androidx.core.view.doOnLayout
34
- import coil3.load
35
- import coil3.request.crossfade
36
- import coil3.size.ViewSizeResolver
37
- import android.view.ViewGroup
38
- import android.view.WindowManager
39
- import androidx.compose.runtime.SideEffect
40
- import androidx.compose.ui.platform.LocalView
41
- import androidx.compose.ui.window.DialogWindowProvider
42
- import androidx.core.view.WindowCompat
43
- import vn.momo.kits.components.Options
44
26
  import vn.momo.kits.const.AppNavigationBar
45
27
  import vn.momo.kits.const.AppStatusBar
46
28
  import vn.momo.kits.utils.readJson
@@ -81,6 +63,8 @@ actual fun getScreenHeight(): Dp {
81
63
  return getScreenDimensions().height.dp + if (getAndroidBuildVersion() >= 35) 0.dp else AppStatusBar.current + AppNavigationBar.current
82
64
  }
83
65
 
66
+ actual fun getAndroidBuildVersion(): Int = Build.VERSION.SDK_INT
67
+
84
68
  @Composable
85
69
  actual fun LottieAnimation(
86
70
  path: String,
@@ -124,147 +108,3 @@ actual fun NativePaint.setColor(color: Color){
124
108
  this.color = color.toArgb()
125
109
  }
126
110
 
127
- actual fun getAndroidBuildVersion(): Int = Build.VERSION.SDK_INT
128
-
129
- @Composable
130
- actual fun NativeImage(
131
- source: Any,
132
- options: Options,
133
- loading: Boolean,
134
- onLoading: () -> Unit,
135
- onSuccess: () -> Unit,
136
- onError: () -> Unit,
137
- ) {
138
- val currentOptions by rememberUpdatedState(options)
139
-
140
- AndroidView(
141
- modifier = Modifier.fillMaxSize(),
142
- factory = { ctx ->
143
- ImageView(ctx).apply {
144
- adjustViewBounds = false
145
-
146
- addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
147
- (v as ImageView).apply {
148
- applyOrDefer(currentOptions.contentScale, currentOptions.alignment)
149
- colorFilter = currentOptions.tintColor?.let {
150
- PorterDuffColorFilter(it.toArgb(), PorterDuff.Mode.SRC_ATOP)
151
- }
152
- }
153
- }
154
- }
155
- },
156
- update = { imageView ->
157
- imageView.alpha = currentOptions.alpha.coerceIn(0f, 1f)
158
- imageView.applyOrDefer(currentOptions.contentScale, currentOptions.alignment)
159
- imageView.colorFilter = currentOptions.tintColor?.let {
160
- PorterDuffColorFilter(it.toArgb(), PorterDuff.Mode.SRC_ATOP)
161
- }
162
-
163
- imageView.load(source) {
164
- crossfade(loading)
165
- size(ViewSizeResolver(imageView))
166
- listener(
167
- onStart = { onLoading() },
168
- onSuccess = { _, _ ->
169
- imageView.applyOrDefer(currentOptions.contentScale, currentOptions.alignment)
170
- imageView.colorFilter = currentOptions.tintColor?.let {
171
- PorterDuffColorFilter(it.toArgb(), PorterDuff.Mode.SRC_ATOP)
172
- }
173
- onSuccess()
174
- },
175
- onError = { _, _ -> onError() }
176
- )
177
- }
178
- }
179
- )
180
- }
181
- private fun ImageView.applyMatrixInternal(
182
- contentScale: ContentScale,
183
- alignment: Alignment
184
- ): Boolean {
185
- val d = drawable ?: return false
186
-
187
- val vw = width - paddingLeft - paddingRight
188
- val vh = height - paddingTop - paddingBottom
189
- if (vw <= 0 || vh <= 0) return false
190
-
191
- val dw = (d.intrinsicWidth).takeIf { it > 0 } ?: return false
192
- val dh = (d.intrinsicHeight).takeIf { it > 0 } ?: return false
193
-
194
- val sxFit = vw.toFloat() / dw.toFloat()
195
- val syFit = vh.toFloat() / dh.toFloat()
196
-
197
- val (sx, sy) = when (contentScale) {
198
- ContentScale.Crop -> maxOf(sxFit, syFit).let { it to it }
199
- ContentScale.Fit -> minOf(sxFit, syFit).let { it to it }
200
- ContentScale.FillWidth -> sxFit to sxFit
201
- ContentScale.FillHeight -> syFit to syFit
202
- ContentScale.FillBounds -> sxFit to syFit
203
- ContentScale.Inside -> minOf(1f, minOf(sxFit, syFit)).let { it to it }
204
- ContentScale.None -> 1f to 1f
205
- else -> maxOf(sxFit, syFit).let { it to it }
206
- }
207
-
208
- val (bx, by) = when (alignment) {
209
- Alignment.TopStart -> -1f to -1f
210
- Alignment.TopCenter -> 0f to -1f
211
- Alignment.TopEnd -> 1f to -1f
212
- Alignment.CenterStart -> -1f to 0f
213
- Alignment.Center -> 0f to 0f
214
- Alignment.CenterEnd -> 1f to 0f
215
- Alignment.BottomStart -> -1f to 1f
216
- Alignment.BottomCenter -> 0f to 1f
217
- Alignment.BottomEnd -> 1f to 1f
218
- else -> 0f to 0f
219
- }
220
-
221
- val scaledW = dw * sx
222
- val scaledH = dh * sy
223
-
224
- val tx = (vw - scaledW) * (bx + 1f) / 2f
225
- val ty = (vh - scaledH) * (by + 1f) / 2f
226
-
227
- if (scaleType != ImageView.ScaleType.MATRIX) {
228
- scaleType = ImageView.ScaleType.MATRIX
229
- }
230
-
231
- val m = imageMatrix ?: Matrix()
232
- m.reset()
233
- m.setScale(sx, sy)
234
- m.postTranslate(tx, ty)
235
- imageMatrix = m
236
- return true
237
- }
238
-
239
- private fun ImageView.applyOrDefer(contentScale: ContentScale, alignment: Alignment) {
240
- if (!applyMatrixInternal(contentScale, alignment)) {
241
- doOnLayout { if (!applyMatrixInternal(contentScale, alignment)) post { applyMatrixInternal(contentScale, alignment) } }
242
- }
243
- }
244
-
245
- @Composable
246
- actual fun ConfigureDialogWindow(decorFitsSystemWindows: Boolean) {
247
- val view = LocalView.current
248
- if (!view.isInEditMode) {
249
- SideEffect {
250
- // In Compose, LocalView.current inside a Dialog is the DialogLayout
251
- // which itself implements DialogWindowProvider.
252
- val window = (view as? DialogWindowProvider)?.window
253
- ?: (view.parent as? DialogWindowProvider)?.window
254
- ?: return@SideEffect
255
-
256
- WindowCompat.setDecorFitsSystemWindows(window, decorFitsSystemWindows)
257
-
258
- if (!decorFitsSystemWindows) {
259
- // Extend layout behind the status bar and navigation bar so the
260
- // dialog scrim covers the full screen uniformly.
261
- window.setLayout(
262
- ViewGroup.LayoutParams.MATCH_PARENT,
263
- ViewGroup.LayoutParams.MATCH_PARENT
264
- )
265
- window.statusBarColor = android.graphics.Color.TRANSPARENT
266
- window.navigationBarColor = android.graphics.Color.TRANSPARENT
267
- }
268
- }
269
- }
270
- }
@@ -78,6 +78,14 @@ data class MiniAppContext(
78
78
  }
79
79
  }
80
80
 
81
+ @Immutable
82
+ data class ComponentInformation(
83
+ val componentName: String? = null,
84
+ val componentId: String? = null,
85
+ val params: Map<String,Any?>? = null,
86
+ val action: String? = null
87
+ )
88
+
81
89
  var IsShowBaseLineDebug = false
82
90
 
83
91
  val ApplicationContext = staticCompositionLocalOf<MiniAppContext?> {
@@ -92,4 +100,8 @@ val AppLanguage = staticCompositionLocalOf<String?> {
92
100
  null
93
101
  }
94
102
 
103
+ val LocalComponentInformation = staticCompositionLocalOf<ComponentInformation?> {
104
+ null
105
+ }
106
+
95
107
  internal val ScaleSizeMaxRate = staticCompositionLocalOf<Float?> { null }
@@ -23,7 +23,7 @@ import vn.momo.kits.modifier.conditional
23
23
 
24
24
 
25
25
  @Composable
26
- fun Badge(label: String = "Label", backgroundColor: Color? = null) {
26
+ fun Badge(label: String = "Label", backgroundColor: Color? = null, modifier: Modifier? = null) {
27
27
  val primaryColors = listOf(
28
28
  Color(0xFFF0F0F0),
29
29
  Color(0xFFEB2F96),
@@ -63,21 +63,23 @@ fun Badge(label: String = "Label", backgroundColor: Color? = null) {
63
63
  }
64
64
  val scaleSize = scaleSize(16f)
65
65
 
66
- Box(
67
- modifier = Modifier
68
- .height(scaleSize.dp)
69
- .widthIn(min = scaleSize.dp)
70
- .background(color = badgeColor, shape = RoundedCornerShape(Radius.M))
71
- .border(width = 1.dp, shape = RoundedCornerShape(Radius.M), color = Colors.black_01)
72
- .conditional(IsShowBaseLineDebug) {
73
- border(1.dp, Colors.blue_03)
74
- }
75
- .padding(horizontal = Spacing.XS), contentAlignment = Alignment.Center
76
- ) {
77
- Text(
78
- text = formatTitle(label),
79
- color = Colors.black_01,
80
- style = Typography.actionXxsBold
81
- )
66
+ if (modifier != null) {
67
+ Box(
68
+ modifier = modifier
69
+ .height(scaleSize.dp)
70
+ .widthIn(min = scaleSize.dp)
71
+ .background(color = badgeColor, shape = RoundedCornerShape(Radius.M))
72
+ .border(width = 1.dp, shape = RoundedCornerShape(Radius.M), color = Colors.black_01)
73
+ .conditional(IsShowBaseLineDebug) {
74
+ border(1.dp, Colors.blue_03)
75
+ }
76
+ .padding(horizontal = Spacing.XS), contentAlignment = Alignment.Center
77
+ ) {
78
+ Text(
79
+ text = formatTitle(label),
80
+ color = Colors.black_01,
81
+ style = Typography.actionXxsBold
82
+ )
83
+ }
82
84
  }
83
85
  }
@@ -0,0 +1,198 @@
1
+ package vn.momo.kits.components
2
+
3
+ import androidx.compose.foundation.Canvas
4
+ import androidx.compose.foundation.background
5
+ import androidx.compose.foundation.border
6
+ import androidx.compose.foundation.layout.Box
7
+ import androidx.compose.foundation.layout.WindowInsets
8
+ import androidx.compose.foundation.layout.asPaddingValues
9
+ import androidx.compose.foundation.layout.fillMaxHeight
10
+ import androidx.compose.foundation.layout.fillMaxSize
11
+ import androidx.compose.foundation.layout.fillMaxWidth
12
+ import androidx.compose.foundation.layout.height
13
+ import androidx.compose.foundation.layout.navigationBars
14
+ import androidx.compose.foundation.layout.padding
15
+ import androidx.compose.foundation.layout.width
16
+ import androidx.compose.runtime.Composable
17
+ import androidx.compose.ui.Alignment
18
+ import androidx.compose.ui.Modifier
19
+ import androidx.compose.ui.geometry.Offset
20
+ import androidx.compose.ui.graphics.Color
21
+ import androidx.compose.ui.graphics.PathEffect
22
+ import androidx.compose.ui.graphics.StrokeCap
23
+ import androidx.compose.ui.unit.dp
24
+ import io.ktor.util.Platform
25
+ import vn.momo.kits.application.IsShowBaseLineDebug
26
+ import vn.momo.kits.const.Colors
27
+ import vn.momo.kits.modifier.conditional
28
+ import vn.momo.kits.platform.getPlatformName
29
+ import vn.momo.kits.platform.getStatusBarHeight
30
+
31
+ /**
32
+ * A debug overlay that draws danger/warning baseline guides on top of the screen.
33
+ *
34
+ * Highlights safe-area boundaries, header regions, and bottom navigation zones
35
+ * using colored solid or dotted lines and semi-transparent red zones.
36
+ *
37
+ * @param enabled When `false` the composable renders nothing. Pass `false` when
38
+ * your QC automation flag is active to suppress the overlay.
39
+ */
40
+ @Composable
41
+ fun BaselineView(enabled: Boolean = true) {
42
+ if (!enabled) return
43
+
44
+ val bottomInsetHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
45
+ val topInset = if (getPlatformName() == "Android") getStatusBarHeight() - 14.dp else getStatusBarHeight()
46
+ val bottomInset = if (getPlatformName() == "iOS") minOf(bottomInsetHeight, 21.dp) else bottomInsetHeight
47
+
48
+ Box(modifier = Modifier.fillMaxSize()) {
49
+ // Danger zones
50
+ Box(
51
+ modifier = Modifier
52
+ .fillMaxWidth()
53
+ .height(topInset)
54
+ .background(Color.Red.copy(alpha = 0.15f))
55
+ )
56
+ Box(
57
+ modifier = Modifier
58
+ .width(12.dp)
59
+ .fillMaxHeight()
60
+ .padding(top = topInset, bottom = bottomInset)
61
+ .background(Color.Red.copy(alpha = 0.15f))
62
+ )
63
+ Box(
64
+ modifier = Modifier
65
+ .width(12.dp)
66
+ .fillMaxHeight()
67
+ .padding(top = topInset, bottom = bottomInset)
68
+ .background(Color.Red.copy(alpha = 0.15f))
69
+ .align(Alignment.TopEnd)
70
+ )
71
+ Box(
72
+ modifier = Modifier
73
+ .fillMaxWidth()
74
+ .height(bottomInset)
75
+ .background(Color.Red.copy(alpha = 0.15f))
76
+ .align(Alignment.BottomCenter)
77
+ )
78
+
79
+ // Danger lines
80
+ BaselineDottedLine(
81
+ Modifier
82
+ .padding(top = topInset)
83
+ .fillMaxWidth(),
84
+ isDotted = false,
85
+ color = Color(0xFFE400FF)
86
+ )
87
+ BaselineDottedLine(
88
+ Modifier
89
+ .padding(top = topInset + 52.dp)
90
+ .fillMaxWidth(),
91
+ color = Color(0xFFE400FF)
92
+ )
93
+ BaselineDottedLine(
94
+ Modifier
95
+ .padding(top = topInset + 104.dp)
96
+ .fillMaxWidth(),
97
+ color = Color(0xFFE400FF)
98
+ )
99
+ BaselineDottedLine(
100
+ Modifier
101
+ .padding(bottom = bottomInset + 64.dp)
102
+ .fillMaxWidth()
103
+ .align(Alignment.BottomCenter),
104
+ color = Color(0xFFE400FF)
105
+ )
106
+ BaselineDottedLine(
107
+ Modifier
108
+ .padding(bottom = bottomInset)
109
+ .fillMaxWidth()
110
+ .align(Alignment.BottomCenter),
111
+ isDotted = false,
112
+ color = Color(0xFFE400FF)
113
+ )
114
+ BaselineDottedLine(
115
+ Modifier
116
+ .padding(start = 12.dp)
117
+ .fillMaxHeight(),
118
+ orientation = BaselineOrientation.Vertical,
119
+ isDotted = false,
120
+ color = Color(0xFFE400FF)
121
+ )
122
+ BaselineDottedLine(
123
+ Modifier
124
+ .padding(end = 12.dp)
125
+ .fillMaxHeight()
126
+ .align(Alignment.BottomEnd),
127
+ orientation = BaselineOrientation.Vertical,
128
+ isDotted = false,
129
+ color = Color(0xFFE400FF)
130
+ )
131
+
132
+ // Warning lines
133
+ BaselineDottedLine(
134
+ Modifier
135
+ .padding(top = topInset + 26.dp)
136
+ .fillMaxWidth(),
137
+ color = Color(0xFFFF7A00)
138
+ )
139
+ BaselineDottedLine(
140
+ Modifier
141
+ .padding(bottom = bottomInset + 56.dp)
142
+ .fillMaxWidth()
143
+ .align(Alignment.BottomCenter),
144
+ color = Color(0xFFFFCC00)
145
+ )
146
+ BaselineDottedLine(
147
+ Modifier
148
+ .padding(bottom = bottomInset + 8.dp)
149
+ .fillMaxWidth()
150
+ .align(Alignment.BottomCenter),
151
+ color = Color(0xFFFFCC00)
152
+ )
153
+
154
+ // Header background warning lines
155
+ BaselineDottedLine(
156
+ Modifier
157
+ .padding(start = 40.dp, top = topInset)
158
+ .height(52.dp),
159
+ color = Color(0xFF00C520),
160
+ orientation = BaselineOrientation.Vertical
161
+ )
162
+ BaselineDottedLine(
163
+ Modifier
164
+ .padding(start = 48.dp, top = topInset)
165
+ .height(52.dp),
166
+ color = Color(0xFF00C520),
167
+ orientation = BaselineOrientation.Vertical
168
+ )
169
+ }
170
+ }
171
+
172
+ enum class BaselineOrientation { Horizontal, Vertical }
173
+
174
+ @Composable
175
+ fun BaselineDottedLine(
176
+ modifier: Modifier = Modifier,
177
+ color: Color = Color.Red,
178
+ orientation: BaselineOrientation = BaselineOrientation.Horizontal,
179
+ isDotted: Boolean = true
180
+ ) {
181
+ Canvas(modifier = modifier) {
182
+ val pathEffect = if (isDotted) PathEffect.dashPathEffect(floatArrayOf(8f, 8f)) else null
183
+ drawLine(
184
+ color = color,
185
+ start = if (orientation == BaselineOrientation.Horizontal) Offset(
186
+ 0f,
187
+ size.height / 2
188
+ ) else Offset(size.width / 2, 0f),
189
+ end = if (orientation == BaselineOrientation.Horizontal) Offset(
190
+ size.width,
191
+ size.height / 2
192
+ ) else Offset(size.width / 2, size.height),
193
+ strokeWidth = 1.dp.toPx(),
194
+ pathEffect = pathEffect,
195
+ cap = StrokeCap.Round,
196
+ )
197
+ }
198
+ }