@momo-kits/native-kits 0.162.32 → 0.162.33-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.
- package/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +190 -0
- package/compose/build.gradle.kts.backup +190 -0
- package/compose/compose.podspec +47 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +150 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +112 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +475 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +214 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +263 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +38 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +299 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +103 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +172 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +372 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +589 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +156 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +207 -0
- package/gradle/libs.versions.toml +69 -0
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/gradle.properties +26 -0
- package/gradlew +252 -0
- package/gradlew.bat +94 -0
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/README.md +120 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.border
|
|
5
|
+
import androidx.compose.foundation.clickable
|
|
6
|
+
import androidx.compose.foundation.layout.Box
|
|
7
|
+
import androidx.compose.foundation.layout.size
|
|
8
|
+
import androidx.compose.foundation.shape.CircleShape
|
|
9
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
10
|
+
import androidx.compose.runtime.Composable
|
|
11
|
+
import androidx.compose.runtime.remember
|
|
12
|
+
import androidx.compose.ui.Alignment
|
|
13
|
+
import androidx.compose.ui.Modifier
|
|
14
|
+
import androidx.compose.ui.draw.clip
|
|
15
|
+
import androidx.compose.ui.graphics.Color
|
|
16
|
+
import androidx.compose.ui.graphics.Shape
|
|
17
|
+
import androidx.compose.ui.unit.Dp
|
|
18
|
+
import androidx.compose.ui.unit.dp
|
|
19
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
20
|
+
import vn.momo.kits.const.AppTheme
|
|
21
|
+
import vn.momo.kits.const.Colors
|
|
22
|
+
import vn.momo.kits.const.Radius
|
|
23
|
+
import vn.momo.kits.modifier.conditional
|
|
24
|
+
|
|
25
|
+
data class IconSpecs(
|
|
26
|
+
val box: Dp,
|
|
27
|
+
val icon: Dp,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
enum class IconSize(val value: IconSpecs) {
|
|
31
|
+
LARGE(IconSpecs(box = 48.dp, icon = 24.dp)),
|
|
32
|
+
MEDIUM(IconSpecs(box = 36.dp, icon = 16.dp)),
|
|
33
|
+
SMALL(IconSpecs(box = 28.dp, icon = 16.dp)),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
enum class IconShape {
|
|
37
|
+
CIRCLE,
|
|
38
|
+
SQUARE,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Square keeps an 8dp rounded corner; circle is fully rounded regardless of size.
|
|
42
|
+
private val squareShape = RoundedCornerShape(Radius.S)
|
|
43
|
+
|
|
44
|
+
private fun IconShape.toShape(): Shape = when (this) {
|
|
45
|
+
IconShape.CIRCLE -> CircleShape
|
|
46
|
+
IconShape.SQUARE -> squareShape
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Composable
|
|
50
|
+
internal fun getIconStyle(type: ButtonType, shape: Shape, color: Color? = null): Modifier {
|
|
51
|
+
val theme = AppTheme.current
|
|
52
|
+
|
|
53
|
+
return remember(type, shape, color, theme) {
|
|
54
|
+
when (type) {
|
|
55
|
+
ButtonType.DISABLED -> Modifier
|
|
56
|
+
.background(theme.colors.background.disable, shape)
|
|
57
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
58
|
+
|
|
59
|
+
ButtonType.PRIMARY -> Modifier
|
|
60
|
+
.background(theme.colors.primary, shape)
|
|
61
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
62
|
+
|
|
63
|
+
ButtonType.SECONDARY -> Modifier
|
|
64
|
+
.background(theme.colors.background.surface, shape)
|
|
65
|
+
.border(1.dp, theme.colors.border.default, shape)
|
|
66
|
+
|
|
67
|
+
ButtonType.OUTLINE -> Modifier
|
|
68
|
+
.background(theme.colors.background.surface, shape)
|
|
69
|
+
.border(1.dp, color ?: theme.colors.primary, shape)
|
|
70
|
+
|
|
71
|
+
ButtonType.TONAL -> Modifier
|
|
72
|
+
.background(theme.colors.background.tonal, shape)
|
|
73
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
74
|
+
|
|
75
|
+
ButtonType.DANGER -> Modifier
|
|
76
|
+
.background(theme.colors.error.primary, shape)
|
|
77
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
78
|
+
|
|
79
|
+
ButtonType.TEXT -> Modifier
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Composable
|
|
85
|
+
internal fun getIconColor(type: ButtonType): Color {
|
|
86
|
+
val theme = AppTheme.current
|
|
87
|
+
return remember(type, theme) {
|
|
88
|
+
getButtonColor(type, theme)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Composable
|
|
93
|
+
fun IconButton(
|
|
94
|
+
onClick: () -> Unit,
|
|
95
|
+
type: ButtonType = ButtonType.PRIMARY,
|
|
96
|
+
size: IconSize = IconSize.SMALL,
|
|
97
|
+
shape: IconShape = IconShape.CIRCLE,
|
|
98
|
+
icon: String = "",
|
|
99
|
+
) {
|
|
100
|
+
val specs = size.value
|
|
101
|
+
|
|
102
|
+
val isEnabled = remember(type) {
|
|
103
|
+
type != ButtonType.DISABLED
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
val boxShape = remember(shape) { shape.toShape() }
|
|
107
|
+
|
|
108
|
+
Box(
|
|
109
|
+
modifier = Modifier
|
|
110
|
+
.size(specs.box)
|
|
111
|
+
.then(getIconStyle(type, boxShape))
|
|
112
|
+
.conditional(IsShowBaseLineDebug) {
|
|
113
|
+
border(1.dp, Colors.blue_03)
|
|
114
|
+
}
|
|
115
|
+
.clip(boxShape)
|
|
116
|
+
.clickable(enabled = isEnabled, onClick = onClick),
|
|
117
|
+
contentAlignment = Alignment.Center
|
|
118
|
+
) {
|
|
119
|
+
Icon(
|
|
120
|
+
source = icon,
|
|
121
|
+
color = getIconColor(type),
|
|
122
|
+
size = specs.icon,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.border
|
|
4
|
+
import androidx.compose.foundation.layout.Box
|
|
5
|
+
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
6
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
7
|
+
import androidx.compose.runtime.Composable
|
|
8
|
+
import androidx.compose.runtime.getValue
|
|
9
|
+
import androidx.compose.runtime.mutableStateOf
|
|
10
|
+
import androidx.compose.runtime.remember
|
|
11
|
+
import androidx.compose.runtime.setValue
|
|
12
|
+
import androidx.compose.ui.Alignment
|
|
13
|
+
import androidx.compose.ui.Modifier
|
|
14
|
+
import androidx.compose.ui.graphics.ColorFilter
|
|
15
|
+
import androidx.compose.ui.graphics.DefaultAlpha
|
|
16
|
+
import androidx.compose.ui.graphics.painter.Painter
|
|
17
|
+
import androidx.compose.ui.layout.ContentScale
|
|
18
|
+
import androidx.compose.ui.semantics.contentDescription
|
|
19
|
+
import androidx.compose.ui.semantics.semantics
|
|
20
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
21
|
+
import androidx.compose.ui.unit.dp
|
|
22
|
+
import coil3.compose.AsyncImage as CoilAsyncImage
|
|
23
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
24
|
+
import vn.momo.kits.const.AppTheme
|
|
25
|
+
import vn.momo.kits.const.Colors
|
|
26
|
+
import vn.momo.kits.gifimage.GifImage
|
|
27
|
+
import vn.momo.kits.gifimage.GifImageState
|
|
28
|
+
import vn.momo.kits.modifier.conditional
|
|
29
|
+
|
|
30
|
+
data class Options(
|
|
31
|
+
val alignment: Alignment = Alignment.TopStart,
|
|
32
|
+
val contentScale: ContentScale = ContentScale.Crop,
|
|
33
|
+
val colorFilter: ColorFilter? = null,
|
|
34
|
+
val alpha: Float = DefaultAlpha,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
enum class ImageState {
|
|
38
|
+
Loading, Success, Error
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Cached domain set for better performance
|
|
42
|
+
private val supportedDomains = setOf(
|
|
43
|
+
"static.momocdn.net",
|
|
44
|
+
"img.mservice.com.vn",
|
|
45
|
+
"cdn.mservice.com.vn",
|
|
46
|
+
"img.mservice.io",
|
|
47
|
+
"image.momocdn.net"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
// Pre-computed size chart for optimal image sizing
|
|
51
|
+
private val sizeChart = mapOf(
|
|
52
|
+
64 to "XXS",
|
|
53
|
+
128 to "XS",
|
|
54
|
+
256 to "S",
|
|
55
|
+
512 to "M",
|
|
56
|
+
768 to "L",
|
|
57
|
+
1024 to "XL",
|
|
58
|
+
2048 to "XXL"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// Optimized size selection using binary search approach
|
|
62
|
+
private fun closestSizeForWidth(width: Float): String {
|
|
63
|
+
val targetWidth = width.toInt()
|
|
64
|
+
return sizeChart.entries
|
|
65
|
+
.minByOrNull { kotlin.math.abs(it.key - targetWidth) }
|
|
66
|
+
?.value ?: "M"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Cached regex for better URL parsing performance
|
|
70
|
+
private val urlHostRegex = Regex("^(?:https?://)?([^:/\\s]+)")
|
|
71
|
+
|
|
72
|
+
private fun getHostFromUrl(url: String): String? {
|
|
73
|
+
return try {
|
|
74
|
+
urlHostRegex.find(url)?.groupValues?.get(1)
|
|
75
|
+
} catch (_: Exception) {
|
|
76
|
+
null
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun isGifUrl(url: String): Boolean {
|
|
81
|
+
val path = url.substringBefore('#').substringBefore('?')
|
|
82
|
+
return path.endsWith(".gif", ignoreCase = true)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private fun GifImageState.toImageState(): ImageState {
|
|
86
|
+
return when (this) {
|
|
87
|
+
GifImageState.Success -> ImageState.Success
|
|
88
|
+
GifImageState.Error -> ImageState.Error
|
|
89
|
+
GifImageState.Loading -> ImageState.Loading
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Optimized query parameter appending
|
|
94
|
+
private fun appendSizeQueryParam(url: String, value: String): String {
|
|
95
|
+
val fragmentIndex = url.indexOf('#')
|
|
96
|
+
val (baseUrl, fragment) = if (fragmentIndex != -1) {
|
|
97
|
+
url.take(fragmentIndex) to url.substring(fragmentIndex)
|
|
98
|
+
} else {
|
|
99
|
+
url to ""
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
val separator = if (baseUrl.contains('?')) "&" else "?"
|
|
103
|
+
return "${baseUrl}${separator}size=$value$fragment"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Cache for processed URLs to avoid repeated calculations
|
|
107
|
+
private val urlCache = mutableMapOf<String, String>()
|
|
108
|
+
|
|
109
|
+
@Composable
|
|
110
|
+
fun Image(
|
|
111
|
+
source: Any,
|
|
112
|
+
modifier: Modifier = Modifier,
|
|
113
|
+
options: Options? = null,
|
|
114
|
+
loading: Boolean = true,
|
|
115
|
+
onStateChanged: (ImageState) -> Unit = {},
|
|
116
|
+
) {
|
|
117
|
+
val imageOptions = remember(options) {
|
|
118
|
+
options ?: Options()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
val density = LocalDensity.current
|
|
122
|
+
|
|
123
|
+
BoxWithConstraints(
|
|
124
|
+
modifier = modifier
|
|
125
|
+
.conditional(IsShowBaseLineDebug) {
|
|
126
|
+
border(1.dp, Colors.blue_03)
|
|
127
|
+
}
|
|
128
|
+
.semantics { contentDescription = "img|$source" },
|
|
129
|
+
) {
|
|
130
|
+
val urlToLoad = remember(source, maxWidth, maxHeight, density) {
|
|
131
|
+
when (source) {
|
|
132
|
+
is String -> {
|
|
133
|
+
// Check cache first
|
|
134
|
+
val cacheKey = "$source|${maxWidth}|${density.density}"
|
|
135
|
+
urlCache[cacheKey] ?: run {
|
|
136
|
+
val processedUrl = processImageUrl(source, maxWidth, density)
|
|
137
|
+
urlCache[cacheKey] = processedUrl
|
|
138
|
+
processedUrl
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else -> source.toString()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
var imageState by remember(urlToLoad) { mutableStateOf(ImageState.Loading) }
|
|
146
|
+
fun updateImageState(state: ImageState) {
|
|
147
|
+
imageState = state
|
|
148
|
+
onStateChanged(state)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (isGifUrl(urlToLoad)) {
|
|
152
|
+
GifImage(
|
|
153
|
+
modifier = Modifier.matchParentSize(),
|
|
154
|
+
url = urlToLoad,
|
|
155
|
+
contentDescription = null,
|
|
156
|
+
contentScale = imageOptions.contentScale,
|
|
157
|
+
alignment = imageOptions.alignment,
|
|
158
|
+
colorFilter = imageOptions.colorFilter,
|
|
159
|
+
alpha = imageOptions.alpha,
|
|
160
|
+
onStateChanged = { updateImageState(it.toImageState()) },
|
|
161
|
+
)
|
|
162
|
+
} else {
|
|
163
|
+
CoilAsyncImage(
|
|
164
|
+
modifier = Modifier.matchParentSize(),
|
|
165
|
+
model = urlToLoad,
|
|
166
|
+
contentDescription = null,
|
|
167
|
+
contentScale = imageOptions.contentScale,
|
|
168
|
+
alignment = imageOptions.alignment,
|
|
169
|
+
colorFilter = imageOptions.colorFilter,
|
|
170
|
+
alpha = imageOptions.alpha,
|
|
171
|
+
onLoading = { updateImageState(ImageState.Loading) },
|
|
172
|
+
onSuccess = { updateImageState(ImageState.Success) },
|
|
173
|
+
onError = { updateImageState(ImageState.Error) },
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
when (imageState) {
|
|
178
|
+
ImageState.Success -> {}
|
|
179
|
+
ImageState.Error -> ImageError()
|
|
180
|
+
ImageState.Loading -> if (loading) Skeleton()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Extracted URL processing logic for better performance
|
|
186
|
+
private fun processImageUrl(url: String, maxWidth: androidx.compose.ui.unit.Dp, density: androidx.compose.ui.unit.Density): String {
|
|
187
|
+
val host = getHostFromUrl(url)
|
|
188
|
+
return if (!isGifUrl(url) && host != null && supportedDomains.contains(host)) {
|
|
189
|
+
val widthPx = with(density) { maxWidth.roundToPx() }
|
|
190
|
+
val pixelRatio = density.density
|
|
191
|
+
val pixelFitForWidth = widthPx * pixelRatio
|
|
192
|
+
val sizeKey = closestSizeForWidth(pixelFitForWidth)
|
|
193
|
+
appendSizeQueryParam(url, sizeKey)
|
|
194
|
+
} else {
|
|
195
|
+
url
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@Composable
|
|
200
|
+
private fun ImageError() {
|
|
201
|
+
val theme = AppTheme.current
|
|
202
|
+
Box(
|
|
203
|
+
modifier = Modifier.fillMaxSize(),
|
|
204
|
+
contentAlignment = Alignment.Center
|
|
205
|
+
) {
|
|
206
|
+
Icon(
|
|
207
|
+
source = "media_fail",
|
|
208
|
+
color = theme.colors.text.disable,
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@Composable
|
|
214
|
+
fun Image(
|
|
215
|
+
source: Painter,
|
|
216
|
+
modifier: Modifier = Modifier,
|
|
217
|
+
colorFilter: ColorFilter? = null,
|
|
218
|
+
alpha: Float = DefaultAlpha,
|
|
219
|
+
contentScale: ContentScale = ContentScale.Crop,
|
|
220
|
+
alignment: Alignment = Alignment.Center,
|
|
221
|
+
) {
|
|
222
|
+
androidx.compose.foundation.Image(
|
|
223
|
+
modifier = modifier,
|
|
224
|
+
painter = source,
|
|
225
|
+
contentDescription = null,
|
|
226
|
+
contentScale = contentScale,
|
|
227
|
+
alignment = alignment,
|
|
228
|
+
colorFilter = colorFilter,
|
|
229
|
+
alpha = alpha,
|
|
230
|
+
)
|
|
231
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.border
|
|
5
|
+
import androidx.compose.foundation.clickable
|
|
6
|
+
import androidx.compose.foundation.layout.*
|
|
7
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
8
|
+
import androidx.compose.runtime.Composable
|
|
9
|
+
import androidx.compose.ui.Modifier
|
|
10
|
+
import androidx.compose.ui.draw.clip
|
|
11
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
12
|
+
import androidx.compose.ui.unit.dp
|
|
13
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
14
|
+
import vn.momo.kits.const.Colors
|
|
15
|
+
import vn.momo.kits.const.Radius
|
|
16
|
+
import vn.momo.kits.const.Spacing
|
|
17
|
+
import vn.momo.kits.const.Typography
|
|
18
|
+
import vn.momo.kits.modifier.conditional
|
|
19
|
+
|
|
20
|
+
enum class InformationState {
|
|
21
|
+
DEFAULT,
|
|
22
|
+
WARNING,
|
|
23
|
+
ERROR
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Composable
|
|
27
|
+
fun Information(
|
|
28
|
+
title: String? = null,
|
|
29
|
+
description: String = "Description",
|
|
30
|
+
state: InformationState = InformationState.DEFAULT,
|
|
31
|
+
image: String? = null,
|
|
32
|
+
onPressAction: (() -> Unit)? = null,
|
|
33
|
+
showIcon: Boolean = true,
|
|
34
|
+
action: String? = null,
|
|
35
|
+
showIconClose: Boolean = true,
|
|
36
|
+
onPressClose: (() -> Unit)? = null,
|
|
37
|
+
) {
|
|
38
|
+
var borderColor = Colors.blue_07
|
|
39
|
+
var backgroundColor = Colors.blue_10
|
|
40
|
+
var color = Colors.blue_03
|
|
41
|
+
var iconSource = "notifications_info"
|
|
42
|
+
|
|
43
|
+
if (state === InformationState.WARNING) {
|
|
44
|
+
borderColor = Colors.yellow_06
|
|
45
|
+
backgroundColor = Colors.yellow_09
|
|
46
|
+
color = Colors.orange_03
|
|
47
|
+
iconSource = "24_notifications_alert_triangle"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (state === InformationState.ERROR) {
|
|
51
|
+
borderColor = Colors.red_07
|
|
52
|
+
backgroundColor = Colors.red_10
|
|
53
|
+
color = Colors.red_03
|
|
54
|
+
iconSource = "24_notifications_alert_octagon"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Column(
|
|
58
|
+
modifier = Modifier.border(
|
|
59
|
+
width = 0.5.dp,
|
|
60
|
+
shape = RoundedCornerShape(Radius.S),
|
|
61
|
+
color = borderColor
|
|
62
|
+
).background(color = backgroundColor)
|
|
63
|
+
.conditional(IsShowBaseLineDebug) {
|
|
64
|
+
border(1.dp, Colors.blue_03)
|
|
65
|
+
}
|
|
66
|
+
.padding(Spacing.M)
|
|
67
|
+
) {
|
|
68
|
+
Row {
|
|
69
|
+
if (image != null) {
|
|
70
|
+
Image(
|
|
71
|
+
source = image,
|
|
72
|
+
modifier = Modifier.padding(end = Spacing.S).width(40.dp).height(40.dp).clip(
|
|
73
|
+
RoundedCornerShape(Radius.S)
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
if (image == null && showIcon) {
|
|
78
|
+
Icon(
|
|
79
|
+
source = iconSource,
|
|
80
|
+
size = 16.dp,
|
|
81
|
+
color = color,
|
|
82
|
+
modifier = Modifier.padding(end = Spacing.S)
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
Column(modifier = Modifier.weight(1f)) {
|
|
86
|
+
if (title != null) {
|
|
87
|
+
Text(title, style = Typography.labelDefaultMedium)
|
|
88
|
+
}
|
|
89
|
+
Text(description, style = Typography.descriptionDefaultRegular)
|
|
90
|
+
}
|
|
91
|
+
if (onPressClose != null && showIconClose) {
|
|
92
|
+
Icon(
|
|
93
|
+
source = "navigation_close",
|
|
94
|
+
size = 16.dp,
|
|
95
|
+
modifier = Modifier.clickable(onClick = onPressClose)
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (action != null && onPressAction != null) {
|
|
100
|
+
Row(modifier = Modifier.padding(top = Spacing.S)) {
|
|
101
|
+
Text(
|
|
102
|
+
action,
|
|
103
|
+
modifier = Modifier.weight(1f).clickable(onClick = onPressAction),
|
|
104
|
+
textAlign = TextAlign.End,
|
|
105
|
+
style = Typography.actionXsBold,
|
|
106
|
+
color = color
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
}
|