@momo-kits/native-kits 0.112.1-beta.9 → 0.113.1-beta.0
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/compose/src/commonMain/kotlin/vn/momo/kits/application/Components.kt +4 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +36 -32
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +4 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +6 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1 -6
- package/ios/Application/Components.swift +223 -0
- package/ios/Application/FloatingButton.swift +148 -0
- package/ios/Application/Screen.swift +175 -0
- package/ios/Input/InputSearch.swift +56 -0
- package/local.properties +2 -2
- package/package.json +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -234
|
@@ -242,12 +242,10 @@ fun Header(
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
headerRight?.invoke()
|
|
250
|
-
}
|
|
245
|
+
Box(Modifier.graphicsLayer {
|
|
246
|
+
alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
|
|
247
|
+
}) {
|
|
248
|
+
headerRight?.invoke()
|
|
251
249
|
}
|
|
252
250
|
}
|
|
253
251
|
|
|
@@ -22,44 +22,49 @@ import vn.momo.kits.const.defaultTheme
|
|
|
22
22
|
import vn.momo.kits.platform.disableOverscroll
|
|
23
23
|
import vn.momo.kits.platform.getStatusBarHeight
|
|
24
24
|
|
|
25
|
+
val PlatformApi = staticCompositionLocalOf<Any?> { null }
|
|
26
|
+
|
|
25
27
|
interface ComposeApi {
|
|
26
|
-
fun request(funcName: String, params:
|
|
27
|
-
fun request(funcName: String, params:
|
|
28
|
-
fun requestCallback(funcName: String, params:
|
|
28
|
+
fun request(funcName: String, params: String?): String
|
|
29
|
+
fun request(funcName: String, params: String?, onResponse: ((String) -> Unit)?): String
|
|
30
|
+
fun requestCallback(funcName: String, params: String?, onResponse: ((String) -> Unit)?)
|
|
29
31
|
fun removeCallback(id: String)
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
val AppNavigator = staticCompositionLocalOf<Navigator> {
|
|
35
|
+
error("no navigator provided!")
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
class Navigator {
|
|
33
|
-
fun push(content: @Composable () -> Unit) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
fun
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
fun push(content: @Composable () -> Unit) {
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fun present(content: @Composable () -> Unit) {
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun reset(content: @Composable () -> Unit) {
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun pop() {
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fun replace(content: @Composable () -> Unit) {
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fun popToTop() {
|
|
57
|
+
}
|
|
58
|
+
|
|
39
59
|
fun showModal(
|
|
40
60
|
onClose: () -> Unit = {},
|
|
41
61
|
canBackgroundClose: Boolean = true,
|
|
42
62
|
content: @Composable () -> Unit,
|
|
43
|
-
) {
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
data class MiniAppContext(
|
|
48
|
-
val icon: String = "",
|
|
49
|
-
val name: String = "",
|
|
50
|
-
val appId: String = "",
|
|
51
|
-
val code: String = "",
|
|
52
|
-
val description: String? = "",
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
val PlatformApi = staticCompositionLocalOf<Any?> { null }
|
|
56
|
-
|
|
57
|
-
val AppNavigator = staticCompositionLocalOf<Navigator> {
|
|
58
|
-
error("no navigator provided!")
|
|
59
|
-
}
|
|
63
|
+
) {
|
|
64
|
+
}
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
fun showBottomSheet(content: @Composable () -> Unit) {
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
@Composable
|
|
@@ -67,8 +72,7 @@ fun ApplicationContainer(
|
|
|
67
72
|
theme: Theme = defaultTheme,
|
|
68
73
|
composeApi: ComposeApi? = null,
|
|
69
74
|
statusBarHeight: Dp? = AppStatusBar.current ?: getStatusBarHeight(),
|
|
70
|
-
|
|
71
|
-
content: @Composable () -> Unit,
|
|
75
|
+
content: @Composable () -> Unit
|
|
72
76
|
) {
|
|
73
77
|
var appTheme by remember { mutableStateOf(theme) }
|
|
74
78
|
val coroutineScope = rememberCoroutineScope()
|
|
@@ -93,7 +97,8 @@ fun ApplicationContainer(
|
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
|
-
}
|
|
100
|
+
}
|
|
101
|
+
catch (e: Exception) {
|
|
97
102
|
print("@@ == NavigationContainer fetch config error $e")
|
|
98
103
|
}
|
|
99
104
|
}
|
|
@@ -104,7 +109,6 @@ fun ApplicationContainer(
|
|
|
104
109
|
PlatformApi provides composeApi,
|
|
105
110
|
AppNavigator provides Navigator(),
|
|
106
111
|
AppStatusBar provides statusBarHeight,
|
|
107
|
-
ApplicationContext provides applicationContext,
|
|
108
112
|
) {
|
|
109
113
|
content()
|
|
110
114
|
}
|
|
@@ -62,7 +62,7 @@ fun Screen(
|
|
|
62
62
|
onContentLayout: ((LayoutCoordinates) -> Unit)? = null,
|
|
63
63
|
useAvoidKeyboard: Boolean = true,
|
|
64
64
|
footer: @Composable (() -> Unit)? = null,
|
|
65
|
-
headerRight: @Composable (() -> Unit)? =
|
|
65
|
+
headerRight: @Composable (() -> Unit)? = null,
|
|
66
66
|
fabProps: FabProps? = null,
|
|
67
67
|
animatedHeader: AnimatedHeader? = null,
|
|
68
68
|
layoutOffset: Dp = 56.dp,
|
|
@@ -17,9 +17,9 @@ enum class DotSize(val size: Int) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
@Composable
|
|
20
|
-
fun BadgeDot(size: DotSize = DotSize.Large
|
|
20
|
+
fun BadgeDot(size: DotSize = DotSize.Large) {
|
|
21
21
|
Box(
|
|
22
|
-
modifier =
|
|
22
|
+
modifier = Modifier
|
|
23
23
|
.size(size.size.dp)
|
|
24
24
|
.border(width = 1.dp, color = Colors.black_01, shape = RoundedCornerShape(Radius.S))
|
|
25
25
|
.background(color = Colors.red_03, shape = RoundedCornerShape(Radius.S))
|
|
@@ -22,10 +22,10 @@ fun Icon(
|
|
|
22
22
|
color: Color? = AppTheme.current.colors.text.default,
|
|
23
23
|
modifier: Modifier = Modifier,
|
|
24
24
|
) {
|
|
25
|
-
val icon =
|
|
26
|
-
source
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
val icon = if (source.contains("https")) {
|
|
26
|
+
source
|
|
27
|
+
} else {
|
|
28
|
+
Icons[source] ?: ""
|
|
29
29
|
}
|
|
30
30
|
Box(modifier = modifier.size(size).semantics {contentDescription = "img|$icon"}) {
|
|
31
31
|
AsyncImage(
|
|
@@ -75,6 +75,7 @@ fun OTPCaret() {
|
|
|
75
75
|
|
|
76
76
|
@Composable
|
|
77
77
|
fun InputOTP(
|
|
78
|
+
value: String = "",
|
|
78
79
|
length: Int? = null,
|
|
79
80
|
errorMessage: String = "",
|
|
80
81
|
errorSpacing: Boolean = false,
|
|
@@ -85,9 +86,9 @@ fun InputOTP(
|
|
|
85
86
|
onBlur: () -> Unit = {},
|
|
86
87
|
dataType: String = "number",
|
|
87
88
|
accessibilityLabel: String? = null,
|
|
89
|
+
modifier: Modifier = Modifier
|
|
88
90
|
) {
|
|
89
91
|
val MAX_LENGTH = 10
|
|
90
|
-
var value by remember { mutableStateOf("") }
|
|
91
92
|
var isFocused by remember { mutableStateOf(false) }
|
|
92
93
|
var isBlurred = false
|
|
93
94
|
|
|
@@ -96,7 +97,6 @@ fun InputOTP(
|
|
|
96
97
|
(dataType != "number" || !text.any { !it.isDigit() }) &&
|
|
97
98
|
(text.length < value.length || value.length < text.length)
|
|
98
99
|
) {
|
|
99
|
-
value = text
|
|
100
100
|
onChangeText(text)
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -108,7 +108,7 @@ fun InputOTP(
|
|
|
108
108
|
onValueChange = handleChangeText,
|
|
109
109
|
singleLine = true,
|
|
110
110
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = if (dataType == "number") KeyboardType.Number else KeyboardType.Ascii),
|
|
111
|
-
modifier =
|
|
111
|
+
modifier = modifier.height(56.dp).onFocusChanged {
|
|
112
112
|
isFocused = it.isFocused
|
|
113
113
|
if (it.isFocused) {
|
|
114
114
|
onFocus()
|
|
@@ -39,15 +39,15 @@ fun getFont(font: String): FontFamily {
|
|
|
39
39
|
fun getFontFamily(fontFamily: String, fontWeight: FontWeight? = FontWeight.Normal): FontFamily {
|
|
40
40
|
val key = "$fontFamily-${fontWeight?.weight}"
|
|
41
41
|
val fontMap = mapOf(
|
|
42
|
-
"SFProText-100" to "sfprotext_thin.
|
|
43
|
-
"SFProText-200" to "sfprotext_ultralight.
|
|
42
|
+
"SFProText-100" to "sfprotext_thin.otf",
|
|
43
|
+
"SFProText-200" to "sfprotext_ultralight.otf",
|
|
44
44
|
"SFProText-300" to "sfprotext_light.ttf",
|
|
45
45
|
"SFProText-400" to "sfprotext_regular.ttf",
|
|
46
46
|
"SFProText-500" to "sfprotext_medium.ttf",
|
|
47
|
-
"SFProText-600" to "
|
|
48
|
-
"SFProText-700" to "
|
|
49
|
-
"SFProText-800" to "
|
|
50
|
-
"SFProText-900" to "
|
|
47
|
+
"SFProText-600" to "sfprotext_semibold.ttf",
|
|
48
|
+
"SFProText-700" to "sfprotext_bold.ttf",
|
|
49
|
+
"SFProText-800" to "sfprotext_heavy.ttf",
|
|
50
|
+
"SFProText-900" to "sfprotext_black.otf",
|
|
51
51
|
|
|
52
52
|
"MoMoSignature-100" to "momosignature.otf",
|
|
53
53
|
"MoMoSignature-200" to "momosignature.otf",
|
|
@@ -1318,10 +1318,5 @@ val Icons = mapOf(
|
|
|
1318
1318
|
"ic_checked" to "https://img.mservice.com.vn/app/img/kits/checked_ic.png",
|
|
1319
1319
|
"ic_minus" to "https://img.mservice.com.vn/app/img/kits/minus.png",
|
|
1320
1320
|
"ic_navigation" to "https://static.momocdn.net/app/img/kits/ic_navigation.png",
|
|
1321
|
-
"ic_money_bag" to "https://static.momocdn.net/app/img/kits/ic_money_bag.png"
|
|
1322
|
-
"ic_support" to "https://static.momocdn.net/app/img/kits/ic_support.png",
|
|
1323
|
-
"navigation_more_icon" to "https://static.momocdn.net/app/img/kits/navigation_more_icon.png",
|
|
1324
|
-
"help_center" to "https://static.momocdn.net/app/img/kits/help-center.png",
|
|
1325
|
-
"pin_star" to "https://static.momocdn.net/app/img/kits/pin_star.png",
|
|
1326
|
-
"pin_star_checked" to "https://static.momocdn.net/app/img/kits/pin_star_checked.png"
|
|
1321
|
+
"ic_money_bag" to "https://static.momocdn.net/app/img/kits/ic_money_bag.png"
|
|
1327
1322
|
)
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Components.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by sophia on 24/1/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import SwiftUI
|
|
9
|
+
|
|
10
|
+
public enum TitlePosition {
|
|
11
|
+
case left, center
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public enum HeaderType {
|
|
15
|
+
case `default`, extended, none
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
public struct AnimatedHeader {
|
|
20
|
+
public var type: HeaderType = .default
|
|
21
|
+
public var composable: (_ scrollState: Int) -> AnyView = { _ in AnyView(EmptyView()) }
|
|
22
|
+
|
|
23
|
+
public init(
|
|
24
|
+
type: HeaderType = .default,
|
|
25
|
+
composable: @escaping (_ scrollState: Int) -> AnyView = { _ in AnyView(EmptyView()) }
|
|
26
|
+
) {
|
|
27
|
+
self.type = type
|
|
28
|
+
self.composable = composable
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func safeAreaTopInset() -> CGFloat {
|
|
33
|
+
if #available(iOS 15.0, *) {
|
|
34
|
+
return UIApplication.shared.connectedScenes
|
|
35
|
+
.compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.top }
|
|
36
|
+
.first ?? 0
|
|
37
|
+
} else {
|
|
38
|
+
return UIApplication.shared.connectedScenes
|
|
39
|
+
.compactMap { ($0 as? UIWindowScene)?.windows.first?.safeAreaInsets.top }
|
|
40
|
+
.first ?? 0
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
public struct HeaderBackground: View {
|
|
46
|
+
var headerType: HeaderType = .default
|
|
47
|
+
var scrollState: CGFloat
|
|
48
|
+
|
|
49
|
+
public var body: some View {
|
|
50
|
+
|
|
51
|
+
let statusBarHeight = safeAreaTopInset()
|
|
52
|
+
|
|
53
|
+
let opacity = 1 - CGFloat(scrollState) / 200
|
|
54
|
+
let backgroundColor = scrollState == 0 ? Color.clear : Colors.black01
|
|
55
|
+
|
|
56
|
+
Group {
|
|
57
|
+
if headerType == .default {
|
|
58
|
+
let height = statusBarHeight + 52
|
|
59
|
+
ZStack {
|
|
60
|
+
|
|
61
|
+
Rectangle()
|
|
62
|
+
.fill(backgroundColor)
|
|
63
|
+
.frame(height: height)
|
|
64
|
+
.shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
|
|
65
|
+
.overlay(Rectangle()
|
|
66
|
+
.fill(LinearGradient(
|
|
67
|
+
gradient: Gradient(colors: [
|
|
68
|
+
Color(red: 1, green: 0.8, blue: 0.87),
|
|
69
|
+
Color(red: 1, green: 0.8, blue: 0.87).opacity(0.5)
|
|
70
|
+
]),
|
|
71
|
+
startPoint: .top,
|
|
72
|
+
endPoint: .bottom))
|
|
73
|
+
.opacity(opacity)
|
|
74
|
+
.frame(height: height))
|
|
75
|
+
.overlay(Rectangle()
|
|
76
|
+
.fill(Colors.black04)
|
|
77
|
+
.frame(height: 1)
|
|
78
|
+
.frame(maxWidth: .infinity)
|
|
79
|
+
.alignmentGuide(.bottom) { _ in 0 }, alignment: .bottom
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
.frame(maxWidth: .infinity)
|
|
83
|
+
|
|
84
|
+
} else if headerType == .extended {
|
|
85
|
+
ZStack {
|
|
86
|
+
Rectangle()
|
|
87
|
+
.fill(LinearGradient(
|
|
88
|
+
gradient: Gradient(colors: [
|
|
89
|
+
Color(red: 1, green: 0.8, blue: 0.87),
|
|
90
|
+
Color(red: 1, green: 0.8, blue: 0.87).opacity(0)
|
|
91
|
+
]),
|
|
92
|
+
startPoint: .top,
|
|
93
|
+
endPoint: .bottom))
|
|
94
|
+
.opacity(opacity)
|
|
95
|
+
.frame(height: 154)
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public struct Header: View {
|
|
105
|
+
var headerType: HeaderType = .default
|
|
106
|
+
var titlePosition: TitlePosition
|
|
107
|
+
var title: String
|
|
108
|
+
var headerRight: (() -> AnyView)?
|
|
109
|
+
var goBack: (() -> Void)?
|
|
110
|
+
var opacity: CGFloat? = nil
|
|
111
|
+
var animatedHeader: AnimatedHeader? = nil
|
|
112
|
+
var scrollState: CGFloat = 0
|
|
113
|
+
var inputSearchProps: InputSearchProps? = nil
|
|
114
|
+
|
|
115
|
+
@State private var colorFraction: CGFloat = 0
|
|
116
|
+
|
|
117
|
+
public init(
|
|
118
|
+
headerType: HeaderType = .default,
|
|
119
|
+
titlePosition: TitlePosition,
|
|
120
|
+
title: String,
|
|
121
|
+
headerRight: (() -> AnyView)? = nil,
|
|
122
|
+
goBack: (() -> Void)? = nil,
|
|
123
|
+
opacity: CGFloat? = nil,
|
|
124
|
+
animatedHeader: AnimatedHeader? = nil,
|
|
125
|
+
scrollState: CGFloat = 0,
|
|
126
|
+
inputSearchProps: InputSearchProps? = nil
|
|
127
|
+
) {
|
|
128
|
+
self.headerType = headerType
|
|
129
|
+
self.titlePosition = titlePosition
|
|
130
|
+
self.title = title
|
|
131
|
+
self.headerRight = headerRight
|
|
132
|
+
self.goBack = goBack
|
|
133
|
+
self.opacity = opacity
|
|
134
|
+
self.animatedHeader = animatedHeader
|
|
135
|
+
self.scrollState = scrollState
|
|
136
|
+
self.inputSearchProps = inputSearchProps
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public var body: some View {
|
|
140
|
+
|
|
141
|
+
let statusBarHeight = safeAreaTopInset()
|
|
142
|
+
let tintIconColor = Colors.black17
|
|
143
|
+
let backgroundButton = Colors.black01.opacity(0.6)
|
|
144
|
+
|
|
145
|
+
if headerType != .none {
|
|
146
|
+
VStack(spacing: 0) {
|
|
147
|
+
ZStack {
|
|
148
|
+
HStack {
|
|
149
|
+
if let goBack = goBack {
|
|
150
|
+
SwiftUI.Button(action: goBack) {
|
|
151
|
+
Circle()
|
|
152
|
+
.stroke(Color.black.opacity(0.2), lineWidth: 0.2)
|
|
153
|
+
.background(Circle().fill(backgroundButton))
|
|
154
|
+
.frame(width: 28, height: 28)
|
|
155
|
+
.overlay(
|
|
156
|
+
Image(systemName: "arrow.backward")
|
|
157
|
+
.foregroundColor(.black)
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Spacer() // Pushes headerRight to the right
|
|
164
|
+
|
|
165
|
+
if let headerRight = headerRight {
|
|
166
|
+
headerRight()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
.frame(height: 52)
|
|
170
|
+
.padding(.horizontal)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
HStack {
|
|
175
|
+
if titlePosition == .left, goBack != nil {
|
|
176
|
+
Spacer().frame(width: 40)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if inputSearchProps != nil {
|
|
180
|
+
if let inputProps = inputSearchProps {
|
|
181
|
+
InputSearch(
|
|
182
|
+
text: inputProps.$text,
|
|
183
|
+
buttonText: inputProps.buttonText,
|
|
184
|
+
showButtonText: inputProps.showButtonText,
|
|
185
|
+
showBorder: inputProps.showBorder,
|
|
186
|
+
placeholder: inputProps.placeholder,
|
|
187
|
+
onChangeText: inputProps.onChangeText,
|
|
188
|
+
onPressButtonText: inputProps.onPressButtonText,
|
|
189
|
+
error: inputProps.error,
|
|
190
|
+
disabled: inputProps.disabled,
|
|
191
|
+
icon: inputProps.icon,
|
|
192
|
+
iconColor: inputProps.iconColor,
|
|
193
|
+
onRightIconPressed: inputProps.onRightIconPressed,
|
|
194
|
+
onFocus: inputProps.onFocus,
|
|
195
|
+
onBlur: inputProps.onBlur,
|
|
196
|
+
loading: inputProps.loading,
|
|
197
|
+
fontWeight: inputProps.fontWeight,
|
|
198
|
+
keyboardType: inputProps.keyboardType
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
Text(title)
|
|
203
|
+
.font(.headline)
|
|
204
|
+
.foregroundColor(tintIconColor)
|
|
205
|
+
.frame(maxWidth: titlePosition == .center ? .infinity : nil, alignment: titlePosition == .center ? .center : .leading)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Spacer()
|
|
209
|
+
}
|
|
210
|
+
.frame(height: 52)
|
|
211
|
+
.padding(.horizontal)
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
.background(Color.clear)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FloatingButton.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by sophia on 6/2/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import SwiftUI
|
|
9
|
+
import Combine
|
|
10
|
+
|
|
11
|
+
public enum FABSize {
|
|
12
|
+
case small
|
|
13
|
+
case `default`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public enum FABPosition {
|
|
17
|
+
case end
|
|
18
|
+
case center
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public struct FabProps {
|
|
22
|
+
let icon: String
|
|
23
|
+
let iconColor: Color?
|
|
24
|
+
let label: String?
|
|
25
|
+
let onClick: () -> Void
|
|
26
|
+
let size: FABSize
|
|
27
|
+
let bottom: CGFloat?
|
|
28
|
+
let scrollOffset: CGFloat?
|
|
29
|
+
let position: FABPosition?
|
|
30
|
+
|
|
31
|
+
public init(
|
|
32
|
+
icon: String,
|
|
33
|
+
iconColor: Color? = nil,
|
|
34
|
+
label: String? = nil,
|
|
35
|
+
onClick: @escaping () -> Void,
|
|
36
|
+
size: FABSize = .default,
|
|
37
|
+
bottom: CGFloat? = nil,
|
|
38
|
+
scrollOffset: CGFloat? = nil,
|
|
39
|
+
position: FABPosition? = .end
|
|
40
|
+
) {
|
|
41
|
+
self.icon = icon
|
|
42
|
+
self.iconColor = iconColor
|
|
43
|
+
self.label = label
|
|
44
|
+
self.onClick = onClick
|
|
45
|
+
self.size = size
|
|
46
|
+
self.bottom = bottom
|
|
47
|
+
self.scrollOffset = scrollOffset
|
|
48
|
+
self.position = position
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public struct FloatingButton: View {
|
|
53
|
+
@State private var lastScrollOffset: CGFloat = 0
|
|
54
|
+
@State private var isExpanded: Bool = false
|
|
55
|
+
let scrollOffset: CGFloat
|
|
56
|
+
let bottomPadding: CGFloat
|
|
57
|
+
let onClick: () -> Void
|
|
58
|
+
let containerColor: Color
|
|
59
|
+
let contentColor: Color
|
|
60
|
+
let icon: String
|
|
61
|
+
let iconColor: Color?
|
|
62
|
+
let text: String?
|
|
63
|
+
let size: FABSize
|
|
64
|
+
let position: FABPosition
|
|
65
|
+
let keyboardOffset: CGFloat
|
|
66
|
+
|
|
67
|
+
public init(
|
|
68
|
+
scrollOffset: CGFloat = 0,
|
|
69
|
+
bottomPadding: CGFloat = 0,
|
|
70
|
+
onClick: @escaping () -> Void,
|
|
71
|
+
containerColor: Color = .blue,
|
|
72
|
+
contentColor: Color = .white,
|
|
73
|
+
icon: String,
|
|
74
|
+
iconColor: Color? = nil,
|
|
75
|
+
text: String? = nil,
|
|
76
|
+
size: FABSize = .default,
|
|
77
|
+
position: FABPosition = .end,
|
|
78
|
+
keyboardOffset: CGFloat = 0
|
|
79
|
+
) {
|
|
80
|
+
self.scrollOffset = scrollOffset
|
|
81
|
+
self.bottomPadding = bottomPadding
|
|
82
|
+
self.onClick = onClick
|
|
83
|
+
self.containerColor = containerColor
|
|
84
|
+
self.contentColor = contentColor
|
|
85
|
+
self.icon = icon
|
|
86
|
+
self.iconColor = iconColor
|
|
87
|
+
self.text = text
|
|
88
|
+
self.size = size
|
|
89
|
+
self.position = position
|
|
90
|
+
self.keyboardOffset = keyboardOffset
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public var body: some View {
|
|
94
|
+
VStack {
|
|
95
|
+
Spacer()
|
|
96
|
+
HStack {
|
|
97
|
+
if position == .center {
|
|
98
|
+
Spacer()
|
|
99
|
+
}
|
|
100
|
+
FloatingButtonContent()
|
|
101
|
+
.padding(.trailing, position == .end ? 12 : 0)
|
|
102
|
+
.padding(.bottom, bottomPadding)
|
|
103
|
+
.offset(y: -keyboardOffset)
|
|
104
|
+
if position == .end {
|
|
105
|
+
Spacer()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
.onReceive(Just(scrollOffset)) { newValue in
|
|
110
|
+
withAnimation(.easeInOut(duration: 0.3)) {
|
|
111
|
+
isExpanded = newValue > lastScrollOffset
|
|
112
|
+
lastScrollOffset = newValue
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@ViewBuilder
|
|
118
|
+
private func FloatingButtonContent() -> some View {
|
|
119
|
+
SwiftUI.Button(action: onClick) {
|
|
120
|
+
HStack {
|
|
121
|
+
Image(systemName: icon)
|
|
122
|
+
.foregroundColor(iconColor ?? .white)
|
|
123
|
+
.font(size == .small ? .system(size: 12) : .system(size: 24))
|
|
124
|
+
|
|
125
|
+
if isExpanded, let text = text {
|
|
126
|
+
Text(text)
|
|
127
|
+
.foregroundColor(.white)
|
|
128
|
+
.transition(.asymmetric(insertion: expandAnimation, removal: collapseAnimation))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
.padding(.horizontal, isExpanded ? 12 : 0)
|
|
132
|
+
.frame(width: isExpanded ? 80 : (size == .small ? 36 : 48), height: size == .small ? 36 : 48)
|
|
133
|
+
.background(containerColor)
|
|
134
|
+
.cornerRadius(24)
|
|
135
|
+
.shadow(radius: 4)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private var expandAnimation: AnyTransition {
|
|
140
|
+
AnyTransition.opacity.animation(.easeIn(duration: 0.2))
|
|
141
|
+
.combined(with: .move(edge: .leading))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private var collapseAnimation: AnyTransition {
|
|
145
|
+
AnyTransition.opacity.animation(.easeOut(duration: 0.5))
|
|
146
|
+
.combined(with: .move(edge: .trailing))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Screen.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by sophia on 5/2/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import SwiftUI
|
|
9
|
+
|
|
10
|
+
public struct Screen<Content: View>: View {
|
|
11
|
+
var backgroundColor: Color?
|
|
12
|
+
var isBack: Bool
|
|
13
|
+
var headerType: HeaderType
|
|
14
|
+
var verticalAlignment: VerticalAlignment
|
|
15
|
+
var horizontalAlignment: HorizontalAlignment
|
|
16
|
+
var title: String
|
|
17
|
+
var titlePosition: TitlePosition
|
|
18
|
+
var goBack: (() -> Void)?
|
|
19
|
+
var scrollable: Bool
|
|
20
|
+
var onContentLayout: ((CGRect) -> Void)?
|
|
21
|
+
var useAvoidKeyboard: Bool
|
|
22
|
+
var footer: (() -> AnyView)?
|
|
23
|
+
var headerRight: (() -> AnyView)?
|
|
24
|
+
var animatedHeader: AnimatedHeader? = nil
|
|
25
|
+
var layoutOffset: CGFloat
|
|
26
|
+
var inputSearchProps: InputSearchProps? = nil
|
|
27
|
+
var fabProps: FabProps? = nil
|
|
28
|
+
var content: () -> Content
|
|
29
|
+
|
|
30
|
+
public init(
|
|
31
|
+
backgroundColor: Color? = nil,
|
|
32
|
+
isBack: Bool = true,
|
|
33
|
+
headerType: HeaderType = .default,
|
|
34
|
+
verticalAlignment: VerticalAlignment = .top,
|
|
35
|
+
horizontalAlignment: HorizontalAlignment = .leading,
|
|
36
|
+
title: String = "Stack",
|
|
37
|
+
titlePosition: TitlePosition = .center,
|
|
38
|
+
goBack: (() -> Void)? = nil,
|
|
39
|
+
scrollable: Bool = true,
|
|
40
|
+
onContentLayout: ((CGRect) -> Void)? = nil,
|
|
41
|
+
useAvoidKeyboard: Bool = true,
|
|
42
|
+
footer: (() -> AnyView)? = nil,
|
|
43
|
+
headerRight: (() -> AnyView)? = nil,
|
|
44
|
+
animatedHeader: AnimatedHeader? = nil,
|
|
45
|
+
layoutOffset: CGFloat = 56,
|
|
46
|
+
inputSearchProps: InputSearchProps? = nil,
|
|
47
|
+
fabProps: FabProps? = nil,
|
|
48
|
+
@ViewBuilder content: @escaping () -> Content
|
|
49
|
+
) {
|
|
50
|
+
self.backgroundColor = backgroundColor
|
|
51
|
+
self.isBack = isBack
|
|
52
|
+
self.headerType = headerType
|
|
53
|
+
self.verticalAlignment = verticalAlignment
|
|
54
|
+
self.horizontalAlignment = horizontalAlignment
|
|
55
|
+
self.title = title
|
|
56
|
+
self.titlePosition = titlePosition
|
|
57
|
+
self.goBack = goBack
|
|
58
|
+
self.scrollable = scrollable
|
|
59
|
+
self.onContentLayout = onContentLayout
|
|
60
|
+
self.useAvoidKeyboard = useAvoidKeyboard
|
|
61
|
+
self.footer = footer
|
|
62
|
+
self.headerRight = headerRight
|
|
63
|
+
self.animatedHeader = animatedHeader
|
|
64
|
+
self.layoutOffset = layoutOffset
|
|
65
|
+
self.inputSearchProps = inputSearchProps
|
|
66
|
+
self.fabProps = fabProps
|
|
67
|
+
self.content = content
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@State private var scrollOffset: CGFloat = 0
|
|
71
|
+
|
|
72
|
+
public var body: some View {
|
|
73
|
+
GeometryReader { geometry in
|
|
74
|
+
let safeAreaInsets = geometry.safeAreaInsets
|
|
75
|
+
let statusBarHeight = safeAreaInsets.top
|
|
76
|
+
let keyboardHeight: CGFloat = 0
|
|
77
|
+
let keyboardSize: CGFloat = useAvoidKeyboard ? max(keyboardHeight, 0) : 0
|
|
78
|
+
|
|
79
|
+
ZStack {
|
|
80
|
+
(backgroundColor ?? Color.white).edgesIgnoringSafeArea(.all)
|
|
81
|
+
|
|
82
|
+
VStack(spacing: 0) {
|
|
83
|
+
ZStack {
|
|
84
|
+
if animatedHeader == nil {
|
|
85
|
+
HeaderBackground(headerType: headerType, scrollState: scrollOffset)
|
|
86
|
+
} else {
|
|
87
|
+
Color.white.opacity(Double(min(scrollOffset / 114, 1)))
|
|
88
|
+
.overlay(HeaderBackground(headerType: headerType, scrollState: scrollOffset))
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Header(
|
|
93
|
+
headerType: headerType,
|
|
94
|
+
titlePosition: titlePosition,
|
|
95
|
+
title: title,
|
|
96
|
+
headerRight: headerRight,
|
|
97
|
+
goBack: goBack,
|
|
98
|
+
opacity: min(scrollOffset / 114, 1),
|
|
99
|
+
animatedHeader: animatedHeader,
|
|
100
|
+
scrollState: scrollOffset,
|
|
101
|
+
inputSearchProps: inputSearchProps
|
|
102
|
+
).padding(.bottom, headerType == .extended ? 0 : -safeAreaTopInset())
|
|
103
|
+
}.padding(.top, -safeAreaTopInset())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
VStack(alignment: horizontalAlignment, spacing: 0) {
|
|
107
|
+
if animatedHeader != nil {
|
|
108
|
+
Spacer().frame(height: statusBarHeight + 52 + layoutOffset)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if scrollable {
|
|
112
|
+
ScrollView(.vertical, showsIndicators: false) {
|
|
113
|
+
VStack {
|
|
114
|
+
content()
|
|
115
|
+
}
|
|
116
|
+
.background(GeometryReader { geo -> Color in
|
|
117
|
+
DispatchQueue.main.async {
|
|
118
|
+
self.scrollOffset = -geo.frame(in: .global).origin.y
|
|
119
|
+
}
|
|
120
|
+
return Color.clear
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
content()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
.padding(.bottom, keyboardSize)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if let footerView = footer?() {
|
|
131
|
+
ZStack(alignment: .top) {
|
|
132
|
+
Rectangle()
|
|
133
|
+
.fill(Colors.black20)
|
|
134
|
+
.frame(height: 4)
|
|
135
|
+
.shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
|
|
136
|
+
|
|
137
|
+
Footer(footerView: footerView, keyboardSize: keyboardSize)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if let fabProps = fabProps {
|
|
142
|
+
FloatingButton(
|
|
143
|
+
scrollOffset: scrollOffset,
|
|
144
|
+
bottomPadding: fabProps.bottom ?? 16,
|
|
145
|
+
onClick: fabProps.onClick,
|
|
146
|
+
containerColor: .blue,
|
|
147
|
+
contentColor: .white,
|
|
148
|
+
icon: fabProps.icon,
|
|
149
|
+
iconColor: fabProps.iconColor,
|
|
150
|
+
text: fabProps.label,
|
|
151
|
+
size: fabProps.size,
|
|
152
|
+
position: fabProps.position ?? .end,
|
|
153
|
+
keyboardOffset: keyboardSize
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
struct Footer: View {
|
|
164
|
+
var footerView: AnyView
|
|
165
|
+
var keyboardSize: CGFloat
|
|
166
|
+
|
|
167
|
+
var body: some View {
|
|
168
|
+
VStack {
|
|
169
|
+
footerView
|
|
170
|
+
}
|
|
171
|
+
.padding(.bottom, keyboardSize)
|
|
172
|
+
.background(Colors.black01)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
@@ -37,6 +37,62 @@ struct RenderRightIconSearch: View {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
public struct InputSearchProps {
|
|
41
|
+
@Binding var text: String
|
|
42
|
+
var buttonText: String = ""
|
|
43
|
+
var showButtonText: Bool = false
|
|
44
|
+
var showBorder: Bool = true
|
|
45
|
+
var placeholder: String = ""
|
|
46
|
+
var onChangeText: (String) -> Void = { _ in }
|
|
47
|
+
var onPressButtonText: () -> Void = {}
|
|
48
|
+
var error: String = ""
|
|
49
|
+
var disabled: Bool = false
|
|
50
|
+
var icon: String = ""
|
|
51
|
+
var iconColor: Color = Colors.text
|
|
52
|
+
var onRightIconPressed: () -> Void = {}
|
|
53
|
+
var onFocus: () -> Void = {}
|
|
54
|
+
var onBlur: () -> Void = {}
|
|
55
|
+
var loading: Bool = false
|
|
56
|
+
var fontWeight: Font.Weight = .regular
|
|
57
|
+
var keyboardType: UIKeyboardType = .default
|
|
58
|
+
|
|
59
|
+
public init(text: Binding<String>,
|
|
60
|
+
buttonText: String = "",
|
|
61
|
+
showButtonText: Bool = false,
|
|
62
|
+
showBorder: Bool = true,
|
|
63
|
+
placeholder: String = "",
|
|
64
|
+
onChangeText: @escaping (String) -> Void = { _ in },
|
|
65
|
+
onPressButtonText: @escaping () -> Void = {},
|
|
66
|
+
error: String = "",
|
|
67
|
+
disabled: Bool = false,
|
|
68
|
+
icon: String = "",
|
|
69
|
+
iconColor: Color = Colors.text,
|
|
70
|
+
onRightIconPressed: @escaping () -> Void = {},
|
|
71
|
+
onFocus: @escaping () -> Void = {},
|
|
72
|
+
onBlur: @escaping () -> Void = {},
|
|
73
|
+
loading: Bool = false,
|
|
74
|
+
fontWeight: Font.Weight = .regular,
|
|
75
|
+
keyboardType: UIKeyboardType = .default) {
|
|
76
|
+
self._text = text
|
|
77
|
+
self.buttonText = buttonText
|
|
78
|
+
self.showButtonText = showButtonText
|
|
79
|
+
self.showBorder = showBorder
|
|
80
|
+
self.placeholder = placeholder
|
|
81
|
+
self.onChangeText = onChangeText
|
|
82
|
+
self.onPressButtonText = onPressButtonText
|
|
83
|
+
self.error = error
|
|
84
|
+
self.disabled = disabled
|
|
85
|
+
self.icon = icon
|
|
86
|
+
self.iconColor = iconColor
|
|
87
|
+
self.onRightIconPressed = onRightIconPressed
|
|
88
|
+
self.onFocus = onFocus
|
|
89
|
+
self.onBlur = onBlur
|
|
90
|
+
self.loading = loading
|
|
91
|
+
self.fontWeight = fontWeight
|
|
92
|
+
self.keyboardType = keyboardType
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
40
96
|
// MARK: - InputSearch
|
|
41
97
|
|
|
42
98
|
public struct InputSearch: View {
|
package/local.properties
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
# Location of the SDK. This is only used by Gradle.
|
|
5
5
|
# For customization when using a Version Control System, please read the
|
|
6
6
|
# header note.
|
|
7
|
-
#
|
|
8
|
-
sdk.dir=/Users/
|
|
7
|
+
#Wed Aug 21 14:20:12 ICT 2024
|
|
8
|
+
sdk.dir=/Users/huynhdung/Library/Android/sdk
|
package/package.json
CHANGED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.application
|
|
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.Arrangement
|
|
7
|
-
import androidx.compose.foundation.layout.Box
|
|
8
|
-
import androidx.compose.foundation.layout.Row
|
|
9
|
-
import androidx.compose.foundation.layout.height
|
|
10
|
-
import androidx.compose.foundation.layout.offset
|
|
11
|
-
import androidx.compose.foundation.layout.padding
|
|
12
|
-
import androidx.compose.foundation.layout.size
|
|
13
|
-
import androidx.compose.foundation.layout.width
|
|
14
|
-
import androidx.compose.foundation.shape.CircleShape
|
|
15
|
-
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
16
|
-
import androidx.compose.runtime.Composable
|
|
17
|
-
import androidx.compose.runtime.getValue
|
|
18
|
-
import androidx.compose.runtime.mutableStateOf
|
|
19
|
-
import androidx.compose.runtime.remember
|
|
20
|
-
import androidx.compose.runtime.setValue
|
|
21
|
-
import androidx.compose.ui.Alignment
|
|
22
|
-
import androidx.compose.ui.Modifier
|
|
23
|
-
import androidx.compose.ui.draw.clip
|
|
24
|
-
import androidx.compose.ui.graphics.Color
|
|
25
|
-
import androidx.compose.ui.unit.dp
|
|
26
|
-
import vn.momo.kits.components.BadgeDot
|
|
27
|
-
import vn.momo.kits.components.DotSize
|
|
28
|
-
import vn.momo.kits.components.Icon
|
|
29
|
-
import vn.momo.kits.const.Colors
|
|
30
|
-
|
|
31
|
-
data class HeaderRightData(
|
|
32
|
-
val useOnBoarding: Boolean = false,
|
|
33
|
-
val buttonOnBoarding: String? = null,
|
|
34
|
-
val onPress: (() -> Unit)? = null,
|
|
35
|
-
val tintColor: Color = Colors.black_20,
|
|
36
|
-
val preventClose: Any? = null,
|
|
37
|
-
val useShortcut: Boolean = false,
|
|
38
|
-
val useMore: Boolean = false,
|
|
39
|
-
val tools: List<ToolGroup> = emptyList(),
|
|
40
|
-
val appId: String = ""
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
data class Tool(
|
|
44
|
-
val key: String,
|
|
45
|
-
val icon: String,
|
|
46
|
-
val onPress: () -> Unit,
|
|
47
|
-
val showBadge: Boolean = false
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
data class ToolGroup(
|
|
51
|
-
val items: List<Tool>
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
data class NavigationButtonConfig(
|
|
55
|
-
val icon: String,
|
|
56
|
-
val onPress: () -> Unit
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
object Spacing {
|
|
60
|
-
val M = 16.dp
|
|
61
|
-
val S = 8.dp
|
|
62
|
-
val XXS = 2.dp
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@Composable
|
|
66
|
-
fun HeaderRight(
|
|
67
|
-
headerRight: HeaderRightData? = null,
|
|
68
|
-
) {
|
|
69
|
-
Row(
|
|
70
|
-
verticalAlignment = Alignment.CenterVertically,
|
|
71
|
-
) {
|
|
72
|
-
ToolkitHeaderRight(headerRight = headerRight)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
@Composable
|
|
77
|
-
fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
|
|
78
|
-
var isFavorite by remember { mutableStateOf(false) }
|
|
79
|
-
val isLoading by remember { mutableStateOf(false) }
|
|
80
|
-
val api = PlatformApi.current as? ComposeApi
|
|
81
|
-
val context = ApplicationContext.current
|
|
82
|
-
|
|
83
|
-
fun onPressShortcut() {
|
|
84
|
-
api?.request("onToolAction", mapOf("item" to mapOf("key" to "onFavorite"))) {
|
|
85
|
-
isFavorite = !isFavorite
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
fun onPressHelpCenter() {
|
|
90
|
-
val paramMap = mapOf(
|
|
91
|
-
"appId" to context?.appId,
|
|
92
|
-
"code" to context?.code,
|
|
93
|
-
"name" to context?.name,
|
|
94
|
-
"icon" to context?.icon,
|
|
95
|
-
"description" to context?.description
|
|
96
|
-
)
|
|
97
|
-
api?.request("showHelpCenter", paramMap, { _ -> })
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
fun onPressClose() {
|
|
101
|
-
api?.request("dismiss", "")
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
fun onPressMore() {
|
|
105
|
-
val params = listOf(
|
|
106
|
-
headerRight?.tools ?: emptyList<ToolGroup>(),
|
|
107
|
-
mapOf(
|
|
108
|
-
"appId" to context?.appId,
|
|
109
|
-
"code" to context?.code,
|
|
110
|
-
"name" to context?.name,
|
|
111
|
-
"icon" to context?.icon,
|
|
112
|
-
"description" to context?.description
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
api?.request("showTools", params, { _ -> })
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
fun getNavigationButtonConfig(
|
|
120
|
-
tools: List<ToolGroup>? = emptyList(),
|
|
121
|
-
useMore: Boolean? = false,
|
|
122
|
-
isFavorite: Boolean = false,
|
|
123
|
-
onPressShortcut: () -> Unit,
|
|
124
|
-
onPressMore: () -> Unit
|
|
125
|
-
): NavigationButtonConfig {
|
|
126
|
-
val totalTools = tools?.sumOf { it.items.size } ?: 0
|
|
127
|
-
var icon = if (isFavorite) "pin_star_checked" else "pin_star"
|
|
128
|
-
var onClickHandler = onPressShortcut
|
|
129
|
-
if (totalTools > 1 || useMore == true) {
|
|
130
|
-
icon = "navigation_more_icon"
|
|
131
|
-
onClickHandler = onPressMore
|
|
132
|
-
} else if (totalTools == 1 && !tools.isNullOrEmpty()) {
|
|
133
|
-
val singleTool = tools.first().items.firstOrNull()
|
|
134
|
-
if (singleTool != null) {
|
|
135
|
-
icon = singleTool.icon
|
|
136
|
-
onClickHandler = {
|
|
137
|
-
singleTool.onPress()
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return NavigationButtonConfig(icon, onClickHandler)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
val navButtonConfig = getNavigationButtonConfig(
|
|
145
|
-
headerRight?.tools,
|
|
146
|
-
headerRight?.useMore,
|
|
147
|
-
isFavorite,
|
|
148
|
-
::onPressShortcut,
|
|
149
|
-
::onPressMore
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
val showBadge = headerRight?.tools?.any { group ->
|
|
153
|
-
group.items.any { it.showBadge }
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
Row(
|
|
157
|
-
verticalAlignment = Alignment.CenterVertically
|
|
158
|
-
) {
|
|
159
|
-
if (headerRight?.useShortcut == true) {
|
|
160
|
-
NavigationButton(
|
|
161
|
-
disabled = isLoading,
|
|
162
|
-
icon = navButtonConfig.icon,
|
|
163
|
-
showBadge = showBadge,
|
|
164
|
-
onClick = navButtonConfig.onPress
|
|
165
|
-
)
|
|
166
|
-
}
|
|
167
|
-
Row(
|
|
168
|
-
verticalAlignment = Alignment.CenterVertically,
|
|
169
|
-
horizontalArrangement = Arrangement.Center,
|
|
170
|
-
modifier = Modifier
|
|
171
|
-
.padding(start = Spacing.S)
|
|
172
|
-
.border(0.2.dp, Color(0x33000000), shape = RoundedCornerShape(14.dp))
|
|
173
|
-
.width(65.dp)
|
|
174
|
-
.height(28.dp)
|
|
175
|
-
.clip(shape = RoundedCornerShape(14.dp))
|
|
176
|
-
.background(Color(0x99FFFFFF))
|
|
177
|
-
) {
|
|
178
|
-
Icon(source = "help_center", size = 20.dp, modifier = Modifier.padding(4.dp).clickable {
|
|
179
|
-
onPressHelpCenter()
|
|
180
|
-
})
|
|
181
|
-
Box(
|
|
182
|
-
modifier = Modifier
|
|
183
|
-
.width(0.5.dp)
|
|
184
|
-
.height(12.dp)
|
|
185
|
-
.background(Colors.black_20)
|
|
186
|
-
)
|
|
187
|
-
Icon(
|
|
188
|
-
source = "16_navigation_close_circle",
|
|
189
|
-
size = 20.dp,
|
|
190
|
-
modifier = Modifier.padding(4.dp).clickable { onPressClose() }
|
|
191
|
-
)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
@Composable
|
|
197
|
-
fun NavigationButton(
|
|
198
|
-
disabled: Boolean,
|
|
199
|
-
icon: String,
|
|
200
|
-
showBadge: Boolean? = false,
|
|
201
|
-
onClick: () -> Unit
|
|
202
|
-
) {
|
|
203
|
-
Box(
|
|
204
|
-
modifier = Modifier
|
|
205
|
-
.size(28.dp)
|
|
206
|
-
.clickable(enabled = !disabled, onClick = onClick)
|
|
207
|
-
) {
|
|
208
|
-
Box(
|
|
209
|
-
modifier = Modifier
|
|
210
|
-
.matchParentSize()
|
|
211
|
-
.clip(CircleShape)
|
|
212
|
-
.background(Color(0x99FFFFFF))
|
|
213
|
-
.border(0.2.dp, Color(0x33000000), CircleShape)
|
|
214
|
-
)
|
|
215
|
-
Box(
|
|
216
|
-
modifier = Modifier.matchParentSize(),
|
|
217
|
-
contentAlignment = Alignment.Center
|
|
218
|
-
) {
|
|
219
|
-
Icon(
|
|
220
|
-
source = icon,
|
|
221
|
-
size = 16.dp
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (showBadge != true) {
|
|
226
|
-
BadgeDot(
|
|
227
|
-
size = DotSize.Small,
|
|
228
|
-
modifier = Modifier
|
|
229
|
-
.align(Alignment.TopEnd)
|
|
230
|
-
.offset(x = -Spacing.XXS, y = -Spacing.XXS)
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|