@momo-kits/native-kits 0.167.1-duo.1-debug → 0.167.1-duo.10-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/CLAUDE.md +32 -0
- package/compose/build.gradle.kts +15 -1
- package/compose/build.gradle.kts.backup +14 -0
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +53 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +13 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +5 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +21 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +4 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +143 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +100 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +22 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +66 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +23 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +81 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +4 -1
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +4 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +86 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +17 -0
- package/ios/Application/ComposeApi.swift +2 -1
- package/ios/Application/{Components.swift → LegacyHeader.swift} +7 -65
- package/ios/Application/Screen.swift +3 -2
- package/ios/Avatar/Avatar.swift +4 -3
- package/ios/Badge/Badge.swift +6 -4
- package/ios/Badge/BadgeDot.swift +1 -1
- package/ios/Badge/BadgeRibbon.swift +0 -1
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
- package/ios/Carousel/Carousel.swift +29 -12
- package/ios/Checkbox/Checkbox.swift +7 -3
- package/ios/Chip/Chip.swift +5 -4
- package/ios/Collapse/Collapse.swift +70 -19
- package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
- package/ios/DateTimePicker/DateTimePicker.swift +54 -20
- package/ios/DateTimePicker/WheelPicker.swift +103 -105
- package/ios/Divider/Divider.swift +37 -4
- package/ios/Extensions/Color++.swift +38 -9
- package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
- package/ios/Extensions/SkeletonUICompat.swift +11 -0
- package/ios/Icon/Icon.swift +21 -23
- package/ios/Icon/Icons.swift +1329 -0
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -23
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +160 -147
- package/ios/Input/InputPhoneNumber.swift +12 -9
- package/ios/Input/InputSearch.swift +102 -75
- package/ios/Input/InputTextArea.swift +128 -145
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +33 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +123 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/ScreenLayout.swift +312 -0
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +14 -2
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +97 -8
- package/ios/Navigation/BottomTab/BottomTab.swift +187 -0
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +15 -7
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +105 -0
- package/ios/Navigation/BottomTab/DuoTabBar.swift +304 -0
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
- package/ios/Navigation/Component/Header.swift +493 -0
- package/ios/Navigation/Component/HeaderBackground.swift +134 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +386 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
- package/ios/Navigation/Component/SideBar.swift +176 -0
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
- package/ios/Navigation/HostBackGestureBridge.swift +57 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +5 -1
- package/ios/Navigation/Navigation.swift +190 -0
- package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +92 -81
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +185 -36
- package/ios/Navigation/StackScreen.swift +782 -0
- package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
- package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
- package/ios/Pagination/PaginationScroll.swift +2 -2
- package/ios/Popup/PopupDisplay.swift +72 -136
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +140 -0
- package/ios/Popup/PopupNotify.swift +56 -73
- package/ios/Popup/PopupPromotion.swift +31 -47
- package/ios/PopupView/FullscreenPopup.swift +13 -19
- package/ios/PopupView/PopupView.swift +1 -1
- package/ios/PopupView/Utils++.swift +0 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +2 -1
- package/ios/Rating/Rating.swift +9 -1
- package/ios/Resources/dot_loading.json +558 -0
- package/ios/Resources/lottie_circle_loader.json +1 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +6 -1
- package/ios/Steps/Steps.swift +8 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -0
- package/ios/Swipeable/SwipeCell.swift +10 -6
- package/ios/Switch/Switch.swift +22 -20
- package/ios/TabView/TabView.swift +102 -21
- package/ios/Tag/Tag.swift +15 -4
- package/ios/Template/Logo/Logo.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +106 -54
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/Text.swift +61 -19
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +2 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/package.json +1 -1
- package/scripts/gen-ios-icons.mjs +36 -0
- package/.claude/settings.local.json +0 -185
- package/ios/Application/HeaderRight.swift +0 -306
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -22
- package/ios/Application/Navigation/component/Header.swift +0 -178
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
- package/ios/Application/StackScreen.swift +0 -492
- package/local.properties +0 -8
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
|
@@ -12,14 +12,14 @@ import SDWebImageSwiftUI
|
|
|
12
12
|
let jsonUrl = "https://static.momocdn.net/app/json/component-kits/design_system.json"
|
|
13
13
|
|
|
14
14
|
public struct TrustBannerData: Decodable {
|
|
15
|
-
let content: [String: String]
|
|
16
|
-
let subContent: [String: String]
|
|
17
|
-
let pciImage: String
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
let icons: [String]
|
|
21
|
-
let
|
|
22
|
-
|
|
15
|
+
public let content: [String: String]
|
|
16
|
+
public let subContent: [String: String]
|
|
17
|
+
public let pciImage: String?
|
|
18
|
+
public let momoImage: String?
|
|
19
|
+
public let sslImage: String?
|
|
20
|
+
public let icons: [String]?
|
|
21
|
+
public let urlConfig: String
|
|
22
|
+
|
|
23
23
|
enum CodingKeys: String, CodingKey {
|
|
24
24
|
case content
|
|
25
25
|
case subContent
|
|
@@ -29,6 +29,38 @@ public struct TrustBannerData: Decodable {
|
|
|
29
29
|
case momoImage
|
|
30
30
|
case urlConfig = "url_config"
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
public init(
|
|
34
|
+
content: [String: String] = ["vi": "", "en": ""],
|
|
35
|
+
subContent: [String: String] = ["vi": "", "en": ""],
|
|
36
|
+
pciImage: String? = nil,
|
|
37
|
+
momoImage: String? = nil,
|
|
38
|
+
sslImage: String? = nil,
|
|
39
|
+
icons: [String]? = nil,
|
|
40
|
+
urlConfig: String = ""
|
|
41
|
+
) {
|
|
42
|
+
self.content = content
|
|
43
|
+
self.subContent = subContent
|
|
44
|
+
self.pciImage = pciImage
|
|
45
|
+
self.momoImage = momoImage
|
|
46
|
+
self.sslImage = sslImage
|
|
47
|
+
self.icons = icons
|
|
48
|
+
self.urlConfig = urlConfig
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Missing keys fall back to the init defaults, like the Compose data class.
|
|
52
|
+
public init(from decoder: Decoder) throws {
|
|
53
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
54
|
+
self.init(
|
|
55
|
+
content: try container.decodeIfPresent([String: String].self, forKey: .content) ?? ["vi": "", "en": ""],
|
|
56
|
+
subContent: try container.decodeIfPresent([String: String].self, forKey: .subContent) ?? ["vi": "", "en": ""],
|
|
57
|
+
pciImage: try container.decodeIfPresent(String.self, forKey: .pciImage),
|
|
58
|
+
momoImage: try container.decodeIfPresent(String.self, forKey: .momoImage),
|
|
59
|
+
sslImage: try container.decodeIfPresent(String.self, forKey: .sslImage),
|
|
60
|
+
icons: try container.decodeIfPresent([String].self, forKey: .icons),
|
|
61
|
+
urlConfig: try container.decodeIfPresent(String.self, forKey: .urlConfig) ?? ""
|
|
62
|
+
)
|
|
63
|
+
}
|
|
32
64
|
}
|
|
33
65
|
|
|
34
66
|
let defaultBanner = TrustBannerData(
|
|
@@ -38,16 +70,16 @@ let defaultBanner = TrustBannerData(
|
|
|
38
70
|
"vi": "Tìm hiểu thêm",
|
|
39
71
|
"en": "Learn more"
|
|
40
72
|
],
|
|
41
|
-
pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/
|
|
73
|
+
pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci_dss.png",
|
|
74
|
+
momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png",
|
|
42
75
|
sslImage: "https://static.momocdn.net/app/img/kits/trustBanner/ssl.png",
|
|
43
|
-
urlConfig: "login_and_security",
|
|
44
76
|
icons: [
|
|
45
77
|
"https://static.momocdn.net/app/img/kits/trustBanner/ic_viettinbank.png",
|
|
46
78
|
"https://static.momocdn.net/app/img/kits/trustBanner/ic_agribank.png",
|
|
47
79
|
"https://static.momocdn.net/app/img/kits/trustBanner/ic_vietcombank.png",
|
|
48
80
|
"https://static.momocdn.net/app/img/kits/trustBanner/ic_bidv.png"
|
|
49
81
|
],
|
|
50
|
-
|
|
82
|
+
urlConfig: "login_and_security"
|
|
51
83
|
)
|
|
52
84
|
|
|
53
85
|
public struct TrustBanner: View {
|
|
@@ -55,66 +87,86 @@ public struct TrustBanner: View {
|
|
|
55
87
|
@Environment(\.localize) private var localize
|
|
56
88
|
|
|
57
89
|
var onPress: ((String)->Void)?
|
|
90
|
+
private let serviceName: String
|
|
91
|
+
private let screenName: String
|
|
92
|
+
private let trackEvent: ((String, [String: String]) -> Void)?
|
|
58
93
|
|
|
59
|
-
public init(
|
|
94
|
+
public init(
|
|
95
|
+
onPress: ((String)->Void)?,
|
|
96
|
+
serviceName: String = "",
|
|
97
|
+
screenName: String = "",
|
|
98
|
+
trackEvent: ((String, [String: String]) -> Void)? = nil
|
|
99
|
+
) {
|
|
60
100
|
self.onPress = onPress
|
|
101
|
+
self.serviceName = serviceName
|
|
102
|
+
self.screenName = screenName
|
|
103
|
+
self.trackEvent = trackEvent
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private var banner: TrustBannerData {
|
|
107
|
+
applicationEnvironment.config?.trustBanner ?? defaultBanner
|
|
61
108
|
}
|
|
62
109
|
|
|
63
110
|
func onPressSecurity() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
111
|
+
trackEvent?("service_component_clicked", [
|
|
112
|
+
"service_name": serviceName,
|
|
113
|
+
"screen_name": screenName,
|
|
114
|
+
"component_name": "logo_trust",
|
|
115
|
+
"url_config": banner.urlConfig
|
|
116
|
+
])
|
|
117
|
+
onPress?(banner.urlConfig)
|
|
67
118
|
}
|
|
68
|
-
|
|
69
|
-
|
|
119
|
+
|
|
70
120
|
public var body: some View {
|
|
71
|
-
HStack(alignment: .
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
121
|
+
HStack(alignment: .top, spacing: 10) {
|
|
122
|
+
if let momoImage = banner.momoImage {
|
|
123
|
+
WebImage(url: URL(string: momoImage))
|
|
124
|
+
.resizable()
|
|
125
|
+
.scaledToFit()
|
|
126
|
+
.frame(width: 64, height: 64)
|
|
127
|
+
}
|
|
128
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
129
|
+
MomoText(
|
|
130
|
+
localize.translateData(banner.content),
|
|
131
|
+
typography: .descriptionDefaultRegular,
|
|
132
|
+
color: Color(hex: "484848"),
|
|
133
|
+
maxLines: 2
|
|
134
|
+
)
|
|
135
|
+
.padding(.bottom, 8)
|
|
136
|
+
HStack(alignment: .top, spacing: 0) {
|
|
86
137
|
HStack(spacing: 0) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
138
|
+
MomoText(
|
|
139
|
+
localize.translateData(banner.subContent),
|
|
140
|
+
typography: .actionXsBold,
|
|
141
|
+
color: Colors.pink03,
|
|
142
|
+
maxLines: 2
|
|
143
|
+
)
|
|
94
144
|
Icon(source: "arrow_chevron_right_small", color: Colors.pink03)
|
|
95
|
-
}.onTapGesture {
|
|
96
|
-
onPressSecurity()
|
|
97
145
|
}
|
|
98
|
-
|
|
99
|
-
Spacer()
|
|
100
|
-
HStack{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
146
|
+
|
|
147
|
+
Spacer(minLength: 0)
|
|
148
|
+
HStack(spacing: 0) {
|
|
149
|
+
if let pciImage = banner.pciImage {
|
|
150
|
+
WebImage(url: URL(string: pciImage))
|
|
151
|
+
.resizable()
|
|
152
|
+
.scaledToFit()
|
|
153
|
+
.frame(width: 27, height: 20)
|
|
154
|
+
.padding(.trailing, 4)
|
|
155
|
+
}
|
|
156
|
+
if let sslImage = banner.sslImage {
|
|
157
|
+
WebImage(url: URL(string: sslImage))
|
|
158
|
+
.resizable()
|
|
159
|
+
.scaledToFit()
|
|
160
|
+
.frame(width: 52, height: 20)
|
|
161
|
+
}
|
|
109
162
|
}
|
|
110
163
|
}
|
|
111
|
-
|
|
112
164
|
}
|
|
113
|
-
|
|
114
165
|
}
|
|
115
166
|
.padding(.all, 12)
|
|
116
167
|
.background(Color(hex: "F2F8FF"))
|
|
117
168
|
.cornerRadius(12)
|
|
169
|
+
.contentShape(Rectangle())
|
|
118
170
|
.onTapGesture {
|
|
119
171
|
onPressSecurity()
|
|
120
172
|
}
|
package/ios/Title/Title.swift
CHANGED
|
@@ -28,7 +28,6 @@ public enum TitleButtonSize {
|
|
|
28
28
|
|
|
29
29
|
public struct Title: View {
|
|
30
30
|
@Environment(\.appTheme) private var theme
|
|
31
|
-
@Environment(\.momoFontScale) private var fontScale
|
|
32
31
|
|
|
33
32
|
private let title: String
|
|
34
33
|
private let type: TitleType
|
|
@@ -116,15 +115,9 @@ public struct Title: View {
|
|
|
116
115
|
}
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
private var titleFontWeight: Font.Weight {
|
|
120
|
-
switch type {
|
|
121
|
-
case .card: return .regular
|
|
122
|
-
case .section: return .bold
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
118
|
private var titleMaxLines: Int {
|
|
127
|
-
|
|
119
|
+
if textOnly { return Int.max }
|
|
120
|
+
return (showTrailingAction || badgeLabel != nil) ? 1 : 2
|
|
128
121
|
}
|
|
129
122
|
|
|
130
123
|
private var iconVerticalAlignment: VerticalAlignment {
|
|
@@ -151,14 +144,10 @@ public struct Title: View {
|
|
|
151
144
|
}
|
|
152
145
|
}
|
|
153
146
|
|
|
147
|
+
// Every Title variant is bold in the theme font; `headerDefaultBold` only supplies the weight.
|
|
154
148
|
private var titleText: some View {
|
|
155
|
-
|
|
156
|
-
.
|
|
157
|
-
.ignoreBoldTextSetting()
|
|
158
|
-
.foregroundColor(theme.colors.text.default)
|
|
159
|
-
.lineSpacing(fontScale(titleLineHeight) - fontScale(titleFontSize))
|
|
160
|
-
.lineLimit(titleMaxLines)
|
|
161
|
-
.truncationMode(.tail)
|
|
149
|
+
MomoText(title, typography: .headerDefaultBold, maxLines: titleMaxLines)
|
|
150
|
+
.fontSize(titleFontSize, lineHeight: titleLineHeight)
|
|
162
151
|
}
|
|
163
152
|
|
|
164
153
|
private var fullLayout: some View {
|
|
@@ -237,7 +226,7 @@ public struct Title: View {
|
|
|
237
226
|
}
|
|
238
227
|
}
|
|
239
228
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
240
|
-
.padding(
|
|
229
|
+
.padding(.top, type == .section ? Spacing.S : 0)
|
|
241
230
|
}
|
|
242
231
|
}
|
|
243
232
|
|
|
@@ -3,7 +3,7 @@ import SwiftUI
|
|
|
3
3
|
// MARK: - Types
|
|
4
4
|
|
|
5
5
|
/// Tooltip placement relative to the anchor element.
|
|
6
|
-
public enum TooltipPlacement {
|
|
6
|
+
public enum TooltipPlacement: Sendable {
|
|
7
7
|
case top
|
|
8
8
|
case bottom
|
|
9
9
|
case left
|
|
@@ -78,24 +78,37 @@ struct TooltipArrowShape: Shape {
|
|
|
78
78
|
func path(in rect: CGRect) -> Path {
|
|
79
79
|
let w = rect.width
|
|
80
80
|
let h = rect.height
|
|
81
|
-
|
|
81
|
+
let points: [CGPoint]
|
|
82
82
|
switch placement {
|
|
83
83
|
case .top:
|
|
84
|
-
|
|
85
|
-
path.addLine(to: CGPoint(x: w, y: 0))
|
|
86
|
-
path.addLine(to: CGPoint(x: w / 2, y: h))
|
|
84
|
+
points = [CGPoint(x: 0, y: 0), CGPoint(x: w, y: 0), CGPoint(x: w / 2, y: h)]
|
|
87
85
|
case .bottom:
|
|
88
|
-
|
|
89
|
-
path.addLine(to: CGPoint(x: w, y: h))
|
|
90
|
-
path.addLine(to: CGPoint(x: w / 2, y: 0))
|
|
86
|
+
points = [CGPoint(x: 0, y: h), CGPoint(x: w, y: h), CGPoint(x: w / 2, y: 0)]
|
|
91
87
|
case .left:
|
|
92
|
-
|
|
93
|
-
path.addLine(to: CGPoint(x: 0, y: h))
|
|
94
|
-
path.addLine(to: CGPoint(x: w, y: h / 2))
|
|
88
|
+
points = [CGPoint(x: 0, y: 0), CGPoint(x: 0, y: h), CGPoint(x: w, y: h / 2)]
|
|
95
89
|
case .right:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
points = [CGPoint(x: w, y: 0), CGPoint(x: w, y: h), CGPoint(x: 0, y: h / 2)]
|
|
91
|
+
}
|
|
92
|
+
// rounded like Compose's cornerPathEffect: each corner is cut `radius` along both edges
|
|
93
|
+
let radius = TooltipConst.arrowCornerRadius
|
|
94
|
+
func toward(_ from: CGPoint, _ to: CGPoint) -> CGPoint {
|
|
95
|
+
let dx = to.x - from.x
|
|
96
|
+
let dy = to.y - from.y
|
|
97
|
+
let length = (dx * dx + dy * dy).squareRoot()
|
|
98
|
+
guard length > 0 else { return from }
|
|
99
|
+
let distance = min(radius, length / 2)
|
|
100
|
+
return CGPoint(x: from.x + dx / length * distance, y: from.y + dy / length * distance)
|
|
101
|
+
}
|
|
102
|
+
var path = Path()
|
|
103
|
+
for index in 0..<points.count {
|
|
104
|
+
let corner = points[index]
|
|
105
|
+
let start = toward(corner, points[(index + points.count - 1) % points.count])
|
|
106
|
+
if index == 0 {
|
|
107
|
+
path.move(to: start)
|
|
108
|
+
} else {
|
|
109
|
+
path.addLine(to: start)
|
|
110
|
+
}
|
|
111
|
+
path.addQuadCurve(to: toward(corner, points[(index + 1) % points.count]), control: corner)
|
|
99
112
|
}
|
|
100
113
|
path.closeSubpath()
|
|
101
114
|
return path
|
|
@@ -209,6 +222,8 @@ private struct TooltipPositionModifier: ViewModifier {
|
|
|
209
222
|
}
|
|
210
223
|
)
|
|
211
224
|
.offset(x: originX, y: originY)
|
|
225
|
+
// the first pass has no size yet and would flash at the wrong spot
|
|
226
|
+
.opacity(popupSize == .zero ? 0 : 1)
|
|
212
227
|
}
|
|
213
228
|
|
|
214
229
|
private var originX: CGFloat {
|
|
@@ -99,28 +99,47 @@ public extension TypographyStyle {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
private
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
private enum MomoFontFace {
|
|
103
|
+
case system
|
|
104
|
+
case named(String)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Port of Compose `getFontFamily`; SF Pro Text is the iOS system font, so the legacy family is `.system`.
|
|
108
|
+
private func momoFontFace(family: String, weight: Font.Weight) -> MomoFontFace? {
|
|
109
|
+
let isNewFamily = family == "MomoTrustSans" || family == headerFontFamily
|
|
110
|
+
// Outside the whitelist the new DS families drop to the legacy font, as before DS-791.
|
|
111
|
+
if family == "SFProText" || (isNewFamily && !KitDesignSystem.shared.isWhiteList) { return .system }
|
|
112
|
+
// single-file families (Compose maps every weight to the same .otf)
|
|
113
|
+
if family == headerFontFamily || family == "MoMoSignature" { return .named(family) }
|
|
105
114
|
guard family == "MomoTrustSans" else { return nil }
|
|
106
115
|
switch weight {
|
|
107
|
-
case .ultraLight, .thin: return "MomoTrustSans-ExtraLight"
|
|
108
|
-
case .light: return "MomoTrustSans-Light"
|
|
109
|
-
case .regular: return "MomoTrustSans-Regular"
|
|
110
|
-
case .medium: return "MomoTrustSans-Medium"
|
|
111
|
-
case .semibold: return "MomoTrustSans-SemiBold"
|
|
112
|
-
case .bold: return "MomoTrustSans-Bold"
|
|
113
|
-
case .heavy, .black: return "MomoTrustSans-ExtraBold"
|
|
114
|
-
default: return "MomoTrustSans-Regular"
|
|
116
|
+
case .ultraLight, .thin: return .named("MomoTrustSans-ExtraLight")
|
|
117
|
+
case .light: return .named("MomoTrustSans-Light")
|
|
118
|
+
case .regular: return .named("MomoTrustSans-Regular")
|
|
119
|
+
case .medium: return .named("MomoTrustSans-Medium")
|
|
120
|
+
case .semibold: return .named("MomoTrustSans-SemiBold")
|
|
121
|
+
case .bold: return .named("MomoTrustSans-Bold")
|
|
122
|
+
case .heavy, .black: return .named("MomoTrustSans-ExtraBold")
|
|
123
|
+
default: return .named("MomoTrustSans-Regular")
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
/// Header title family (Compose `HEADER_FONT_FAMILY`), whitelist only like the default font.
|
|
128
|
+
let headerFontFamily = "MoMoTrustDisplay"
|
|
129
|
+
|
|
130
|
+
private func momoFont(size: CGFloat, weight: Font.Weight, family: String, fallbackFamily: String? = nil) -> Font {
|
|
131
|
+
switch momoFontFace(family: family, weight: weight) {
|
|
132
|
+
case .system:
|
|
133
|
+
return .system(size: size, weight: weight)
|
|
134
|
+
case .named(let name) where UIFont(name: name, size: size) != nil:
|
|
135
|
+
return .custom(name, fixedSize: size)
|
|
136
|
+
default:
|
|
137
|
+
// Compose falls back to the default font file when a family is missing
|
|
138
|
+
if let fallbackFamily, fallbackFamily != family {
|
|
139
|
+
return momoFont(size: size, weight: weight, family: fallbackFamily)
|
|
140
|
+
}
|
|
121
141
|
return .system(size: size, weight: weight)
|
|
122
142
|
}
|
|
123
|
-
return .custom(name, fixedSize: size)
|
|
124
143
|
}
|
|
125
144
|
|
|
126
145
|
public struct MomoText: View {
|
|
@@ -139,7 +158,12 @@ public struct MomoText: View {
|
|
|
139
158
|
private let letterSpacing: CGFloat?
|
|
140
159
|
private let softWrap: Bool
|
|
141
160
|
private let accessibilityId: String?
|
|
161
|
+
private let fontFamily: String?
|
|
142
162
|
private var highlightText: [String] = []
|
|
163
|
+
private var sizeOverride: (fontSize: CGFloat, lineHeight: CGFloat)?
|
|
164
|
+
|
|
165
|
+
private var fontSize: CGFloat { sizeOverride?.fontSize ?? typography.fontSize }
|
|
166
|
+
private var lineHeight: CGFloat { sizeOverride?.lineHeight ?? typography.lineHeight }
|
|
143
167
|
|
|
144
168
|
public init(
|
|
145
169
|
_ content: String,
|
|
@@ -153,7 +177,8 @@ public struct MomoText: View {
|
|
|
153
177
|
underline: Bool = false,
|
|
154
178
|
letterSpacing: CGFloat? = nil,
|
|
155
179
|
softWrap: Bool = true,
|
|
156
|
-
accessibilityId: String? = nil
|
|
180
|
+
accessibilityId: String? = nil,
|
|
181
|
+
fontFamily: String? = nil
|
|
157
182
|
) {
|
|
158
183
|
self.content = content
|
|
159
184
|
self.typography = typography
|
|
@@ -175,6 +200,7 @@ public struct MomoText: View {
|
|
|
175
200
|
self.letterSpacing = letterSpacing
|
|
176
201
|
self.softWrap = softWrap
|
|
177
202
|
self.accessibilityId = accessibilityId
|
|
203
|
+
self.fontFamily = fontFamily
|
|
178
204
|
}
|
|
179
205
|
|
|
180
206
|
init(_ content: String, typography: TypographyStyle, highlightText: [String]) {
|
|
@@ -182,11 +208,27 @@ public struct MomoText: View {
|
|
|
182
208
|
self.highlightText = highlightText
|
|
183
209
|
}
|
|
184
210
|
|
|
211
|
+
/// Compose's `style.copy(fontSize, lineHeight)`; the weight still comes from `typography`.
|
|
212
|
+
func fontSize(_ fontSize: CGFloat, lineHeight: CGFloat) -> MomoText {
|
|
213
|
+
var text = self
|
|
214
|
+
text.sizeOverride = (fontSize, lineHeight)
|
|
215
|
+
return text
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private var font: Font {
|
|
219
|
+
momoFont(
|
|
220
|
+
size: fontScale(fontSize),
|
|
221
|
+
weight: typography.fontWeight,
|
|
222
|
+
family: fontFamily ?? theme.font,
|
|
223
|
+
fallbackFamily: theme.font
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
185
227
|
// Whole-word matches of `highlightText` render semibold; the longest keyword wins (RN parity).
|
|
186
228
|
private var baseText: SwiftUI.Text {
|
|
187
229
|
let keywords = highlightText.filter { !$0.isEmpty }.sorted { $0.count > $1.count }
|
|
188
230
|
guard !keywords.isEmpty else { return SwiftUI.Text(content) }
|
|
189
|
-
let highlightFont = momoFont(size: fontScale(
|
|
231
|
+
let highlightFont = momoFont(size: fontScale(fontSize), weight: TypographyStyle.headerXsSemibold.fontWeight, family: fontFamily ?? theme.font, fallbackFamily: theme.font)
|
|
190
232
|
func isWordChar(_ character: Character?) -> Bool {
|
|
191
233
|
guard let character else { return false }
|
|
192
234
|
return character.isLetter || character.isNumber || character == "_"
|
|
@@ -223,7 +265,7 @@ public struct MomoText: View {
|
|
|
223
265
|
let isStrikethrough = strikethrough || typography.isStrikethrough
|
|
224
266
|
|
|
225
267
|
var text = baseText
|
|
226
|
-
.font(
|
|
268
|
+
.font(font)
|
|
227
269
|
.foregroundColor(textColor)
|
|
228
270
|
.strikethrough(isStrikethrough, color: textColor)
|
|
229
271
|
.underline(underline, color: textColor)
|
|
@@ -234,7 +276,7 @@ public struct MomoText: View {
|
|
|
234
276
|
|
|
235
277
|
return text
|
|
236
278
|
.multilineTextAlignment(textAlign)
|
|
237
|
-
.lineSpacing(fontScale(
|
|
279
|
+
.lineSpacing(fontScale(lineHeight) - fontScale(fontSize))
|
|
238
280
|
.modifier(LineLimitModifier(range: lineLimitRange))
|
|
239
281
|
.truncationMode(truncationMode)
|
|
240
282
|
.fixedSize(horizontal: !softWrap, vertical: false)
|
|
@@ -80,7 +80,9 @@ public struct Uploader: View {
|
|
|
80
80
|
|
|
81
81
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
82
82
|
HStack(alignment: .top, spacing: Spacing.S) {
|
|
83
|
-
|
|
83
|
+
// Positional like Compose: `UploadImage.id` is a fresh UUID per init, so hosts that
|
|
84
|
+
// rebuild the array each render would otherwise re-create (and reload) every thumbnail.
|
|
85
|
+
ForEach(Array(images.enumerated()), id: \.offset) { index, image in
|
|
84
86
|
UploaderImageItem(
|
|
85
87
|
image: image,
|
|
86
88
|
width: width,
|
package/ios/native-kits.podspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = 'MoMoUIKits'
|
|
3
|
-
s.version = '0.167.1-
|
|
3
|
+
s.version = '0.167.1-beta.4'
|
|
4
4
|
s.summary = 'MoMoUIKits for iOS'
|
|
5
5
|
s.homepage = 'https://momo.vn'
|
|
6
6
|
s.license = { :type => 'MIT' }
|
|
@@ -37,6 +37,7 @@ Pod::Spec.new do |s|
|
|
|
37
37
|
"Layout/**/*.{swift,m,h}",
|
|
38
38
|
"Loader/**/*.{swift,m,h}",
|
|
39
39
|
"Lottie/**/*.{swift,m,h}",
|
|
40
|
+
"Navigation/**/*.{swift,m,h}",
|
|
40
41
|
"OTPKeyboard/**/*.{swift,m,h}",
|
|
41
42
|
"Pagination/**/*.{swift,m,h}",
|
|
42
43
|
"Popup/**/*.{swift,m,h}",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DuoTabDemo.swift
|
|
3
|
+
// Example
|
|
4
|
+
//
|
|
5
|
+
// Mirrors RN `app/screens/IPhoneDuo/DuoTabUsage`: glass rail in the Duo side bar, floating
|
|
6
|
+
// glass bar when unfolded portrait. `useShortcut` gives the tallest side bar header, for the
|
|
7
|
+
// overlap review; `compressTabBar` folds the tab bar instead of the toolbar when the side bar
|
|
8
|
+
// is short (HIG "Tab bar compressed").
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import MoMoUIKits
|
|
12
|
+
import SwiftUI
|
|
13
|
+
|
|
14
|
+
private let swatches = [Colors.pink03, Colors.orange03, Colors.green03]
|
|
15
|
+
private let history = (1...18).map { "Giao dịch #\($0)" }
|
|
16
|
+
|
|
17
|
+
struct DuoTabDemo: View {
|
|
18
|
+
let useShortcut: Bool
|
|
19
|
+
var compressTabBar = false
|
|
20
|
+
|
|
21
|
+
var body: some View {
|
|
22
|
+
if #available(iOS 16.0, *) {
|
|
23
|
+
DuoTabDemoContent(useShortcut: useShortcut, compressTabBar: compressTabBar)
|
|
24
|
+
} else {
|
|
25
|
+
Text("Requires iOS 16+").padding()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@available(iOS 16.0, *)
|
|
31
|
+
private struct DuoTabDemoContent: View {
|
|
32
|
+
let useShortcut: Bool
|
|
33
|
+
let compressTabBar: Bool
|
|
34
|
+
|
|
35
|
+
var body: some View {
|
|
36
|
+
BottomTab(
|
|
37
|
+
items: [
|
|
38
|
+
tab("DuoMoMo", label: "MoMo", icon: "basic_home", title: "MoMo"),
|
|
39
|
+
tab("DuoPromotion", label: "Ưu đãi", icon: "shopping_coupon", title: "Ưu đãi", badge: ""),
|
|
40
|
+
tab("DuoHistory", label: "Lịch sử GD", icon: "time_clock_reset", title: "Lịch sử giao dịch", badge: "3"),
|
|
41
|
+
tab("DuoMe", label: "Tôi", icon: "basic_person", title: "Tôi"),
|
|
42
|
+
],
|
|
43
|
+
floatingButton: BottomTabFloatingButton(icon: "navigation_full_screen", label: "Quét mọi QR", onPress: {}),
|
|
44
|
+
compressionBehavior: compressTabBar ? .tabBar : .toolbar
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private func tab(_ name: String, label: String, icon: String, title: String, badge: String? = nil) -> BottomTabItem {
|
|
49
|
+
BottomTabItem(
|
|
50
|
+
name: name,
|
|
51
|
+
label: label,
|
|
52
|
+
icon: icon,
|
|
53
|
+
badgeLabel: badge,
|
|
54
|
+
options: NavigationOptions(title: title, headerRightData: HeaderRightData(useShortcut: useShortcut)),
|
|
55
|
+
screen: { DuoTabList() }
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private struct DuoTabList: View {
|
|
61
|
+
var body: some View {
|
|
62
|
+
VStack(spacing: 0) {
|
|
63
|
+
ForEach(history.indices, id: \.self) { index in
|
|
64
|
+
if index > 0 { MomoDivider() }
|
|
65
|
+
HStack(spacing: Spacing.M) {
|
|
66
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
67
|
+
.fill(swatches[index % swatches.count])
|
|
68
|
+
.frame(width: Spacing.XL, height: Spacing.XL)
|
|
69
|
+
MomoText(history[index], typography: .bodyDefaultRegular)
|
|
70
|
+
Spacer()
|
|
71
|
+
}
|
|
72
|
+
.padding(.vertical, Spacing.M)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
.padding(.horizontal, Spacing.M)
|
|
76
|
+
.background(Colors.black01)
|
|
77
|
+
.clipShape(RoundedRectangle(cornerRadius: Radius.M))
|
|
78
|
+
.padding(Spacing.M)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -85,6 +85,7 @@ public let ScreenUsages: [ScreenUsage] = [
|
|
|
85
85
|
ScreenUsage(title: "DateTimePicker", destination: AnyView(DateTimePickerDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
|
|
86
86
|
ScreenUsage(title: "Navigation", destination: AnyView(NavigationDemo()), icon: getIcon(icon: "Layout")),
|
|
87
87
|
ScreenUsage(title: "Bottom Tab", destination: AnyView(BottomTabDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
|
|
88
|
+
ScreenUsage(title: "iPhone Duo", destination: AnyView(IPhoneDuoDemo()), icon: getIcon(icon: "Layout")),
|
|
88
89
|
ScreenUsage(title: "Animated Header", destination: AnyView(AnimatedHeaderDemo()), icon: getIcon(icon: "Layout")),
|
|
89
90
|
ScreenUsage(title: "Theme", destination: AnyView(ThemeDemo()), icon: getIcon(icon: "Typography")),
|
|
90
91
|
ScreenUsage(title: "Localize", destination: AnyView(LocalizeDemo()), icon: getIcon(icon: "Typography")),
|