@momo-kits/native-kits 0.163.1-beta.5 → 0.163.1-beta.6-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/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 +123 -0
- package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
- package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -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 +294 -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 +543 -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 +477 -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 +306 -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 +276 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -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 +253 -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 +98 -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 +360 -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 +562 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +154 -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 +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +70 -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 +58 -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/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 +184 -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/Application/Components.swift +16 -0
- package/ios/Application/Localize.swift +277 -0
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +1 -0
- package/ios/Application/Navigation/NavigationContainer.swift +6 -0
- package/ios/Application/Navigation/Overplay/BottomSheet.swift +87 -19
- package/ios/Application/StackScreen.swift +8 -1
- package/ios/Colors+Radius+Spacing/Colors.swift +9 -6
- package/ios/Colors+Radius+Spacing/Spacing.swift +3 -1
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/README.md +120 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +188 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +2 -0
- package/local.properties +8 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
|
@@ -14,6 +14,7 @@ import SwiftUI
|
|
|
14
14
|
public struct NavigationContainer<Initial: View>: View {
|
|
15
15
|
@StateObject private var navigator: Navigator
|
|
16
16
|
@StateObject private var applicationEnvironment: ApplicationEnvironment
|
|
17
|
+
@StateObject private var localize: Localize
|
|
17
18
|
|
|
18
19
|
private let setNavigator: ((Navigator) -> Void)?
|
|
19
20
|
private let composeApi: KitComposeApi?
|
|
@@ -25,6 +26,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
25
26
|
applicationContext: MiniAppContext? = nil,
|
|
26
27
|
composeApi: KitComposeApi? = nil,
|
|
27
28
|
config: KitConfig? = nil,
|
|
29
|
+
localize: Localize? = nil,
|
|
28
30
|
setNavigator: ((Navigator) -> Void)? = nil
|
|
29
31
|
) {
|
|
30
32
|
let nav = Navigator(
|
|
@@ -41,6 +43,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
41
43
|
config: config
|
|
42
44
|
)
|
|
43
45
|
)
|
|
46
|
+
_localize = StateObject(wrappedValue: localize ?? Localize())
|
|
44
47
|
self.composeApi = composeApi
|
|
45
48
|
self.setNavigator = setNavigator
|
|
46
49
|
}
|
|
@@ -55,11 +58,13 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
55
58
|
.environmentObject(navigator)
|
|
56
59
|
.environmentObject(applicationEnvironment)
|
|
57
60
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
61
|
+
.environmentObject(localize)
|
|
58
62
|
.fullScreenCover(item: $navigator.presented) { route in
|
|
59
63
|
screenView(forDialog: route)
|
|
60
64
|
.environmentObject(navigator)
|
|
61
65
|
.environmentObject(applicationEnvironment)
|
|
62
66
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
67
|
+
.environmentObject(localize)
|
|
63
68
|
}
|
|
64
69
|
.overlay {
|
|
65
70
|
if let item = navigator.overplay {
|
|
@@ -67,6 +72,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
67
72
|
.environmentObject(navigator)
|
|
68
73
|
.environmentObject(applicationEnvironment)
|
|
69
74
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
75
|
+
.environmentObject(localize)
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
.onAppear {
|
|
@@ -43,42 +43,84 @@ struct BottomSheetView<Content: View>: View {
|
|
|
43
43
|
let content: () -> Content
|
|
44
44
|
|
|
45
45
|
@EnvironmentObject private var navigator: Navigator
|
|
46
|
+
@Environment(\.appTheme) private var theme
|
|
47
|
+
@Environment(\.applicationEnvironment) private var appEnv
|
|
46
48
|
|
|
47
49
|
/// Vertical translation of the sheet; starts off-screen and animates to 0.
|
|
48
50
|
@State private var sheetOffset: CGFloat = UIScreen.main.bounds.height
|
|
49
|
-
|
|
51
|
+
/// Mirrors Compose's `backgroundAlpha`: only touched by open()/close(), never by drag.
|
|
52
|
+
@State private var backgroundAlpha: CGFloat = 0
|
|
50
53
|
@State private var sheetHeight: CGFloat = 0
|
|
51
54
|
@State private var didAppear = false
|
|
55
|
+
@State private var keyboardHeight: CGFloat = 0
|
|
56
|
+
/// Live drag offset, isolated from `sheetOffset` so a touch-move only invalidates
|
|
57
|
+
/// this gesture-scoped value instead of forcing the whole body (including
|
|
58
|
+
/// `content()`) to reconstruct every frame — mirrors the pattern already used in
|
|
59
|
+
/// `ios/PopupView/PopupView.swift` for smooth drag-to-dismiss.
|
|
60
|
+
@GestureState private var dragTranslation: CGFloat = 0
|
|
52
61
|
|
|
53
62
|
/// Mirrors Compose's `sheetCloseOffset` (100dp drag threshold to dismiss).
|
|
54
63
|
private let closeThreshold: CGFloat = 100
|
|
55
64
|
|
|
65
|
+
/// Mirrors Compose's per-term peak (0.3f). Combined with `backgroundAlpha` the
|
|
66
|
+
/// steady-state (fully open) scrim peaks at 0.6, matching ModalScreen's peak.
|
|
67
|
+
private let maxDynamicAlpha: CGFloat = 0.3
|
|
68
|
+
|
|
56
69
|
private var screenHeight: CGFloat { UIScreen.main.bounds.height }
|
|
57
70
|
private var bottomInset: CGFloat { safeAreaBottomInset() }
|
|
71
|
+
/// While the keyboard is up, it already covers the home-indicator area, so it
|
|
72
|
+
/// replaces (rather than adds to) the usual bottom-inset filler.
|
|
73
|
+
private var effectiveBottomInset: CGFloat { keyboardHeight > 0 ? keyboardHeight : bottomInset }
|
|
58
74
|
|
|
59
75
|
private var backgroundColor: Color {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
isSurface ? theme.colors.background.surface : theme.colors.background.default
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private var effectiveOffset: CGFloat { sheetOffset + dragTranslation }
|
|
80
|
+
|
|
81
|
+
/// Mirrors Compose's `dynamicAlpha`: a plain derived value (not itself animated)
|
|
82
|
+
/// that tracks the current offset live, shrinking toward 0 as the sheet is
|
|
83
|
+
/// dragged down.
|
|
84
|
+
private var dynamicAlpha: CGFloat {
|
|
85
|
+
let denom = sheetHeight > 0 ? sheetHeight : screenHeight
|
|
86
|
+
guard denom > 0 else { return maxDynamicAlpha }
|
|
87
|
+
let progress = min(max(1 - effectiveOffset / denom, 0), 1)
|
|
88
|
+
return progress * maxDynamicAlpha
|
|
63
89
|
}
|
|
64
90
|
|
|
91
|
+
/// Mirrors Compose's `alpha = backgroundAlpha + dynamicAlpha`: additive, so the
|
|
92
|
+
/// scrim peaks at 0.6 at rest and only fades to a 0.3 floor while dragging
|
|
93
|
+
/// (backgroundAlpha holds steady until an explicit close()).
|
|
94
|
+
private var totalScrimAlpha: CGFloat { backgroundAlpha + dynamicAlpha }
|
|
95
|
+
|
|
65
96
|
var body: some View {
|
|
66
97
|
ZStack(alignment: .bottom) {
|
|
67
|
-
// Dimmed scrim
|
|
68
|
-
Color.black.opacity(
|
|
69
|
-
.ignoresSafeArea()
|
|
98
|
+
// Dimmed scrim — additive backgroundAlpha + dynamicAlpha (see above).
|
|
99
|
+
Color.black.opacity(totalScrimAlpha)
|
|
70
100
|
.contentShape(Rectangle())
|
|
71
101
|
.onTapGesture {
|
|
72
102
|
if barrierDismissible { close() }
|
|
73
103
|
}
|
|
74
104
|
|
|
75
105
|
sheet
|
|
76
|
-
.offset(y:
|
|
106
|
+
.offset(y: effectiveOffset)
|
|
77
107
|
}
|
|
78
108
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
109
|
+
.ignoresSafeArea()
|
|
110
|
+
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)) { notification in
|
|
111
|
+
guard let endFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
|
|
112
|
+
withAnimation(.easeOut(duration: 0.25)) {
|
|
113
|
+
keyboardHeight = UIScreen.main.bounds.height - endFrame.origin.y
|
|
114
|
+
}
|
|
115
|
+
}
|
|
79
116
|
.onAppear {
|
|
80
117
|
guard !didAppear else { return }
|
|
81
118
|
didAppear = true
|
|
119
|
+
ScreenTracker.trackPopupDisplayed(
|
|
120
|
+
maxApi: appEnv.maxApi,
|
|
121
|
+
context: appEnv.applicationContext,
|
|
122
|
+
parentScreenName: ScreenTracker.getLastScreenName()
|
|
123
|
+
)
|
|
82
124
|
navigator.registerOverplayDismiss { close() }
|
|
83
125
|
open()
|
|
84
126
|
}
|
|
@@ -103,16 +145,16 @@ struct BottomSheetView<Content: View>: View {
|
|
|
103
145
|
.gesture(dragGesture)
|
|
104
146
|
|
|
105
147
|
Rectangle()
|
|
106
|
-
.fill(
|
|
148
|
+
.fill(theme.colors.border.default)
|
|
107
149
|
.frame(height: 1)
|
|
108
150
|
|
|
109
151
|
content()
|
|
110
152
|
|
|
111
153
|
backgroundColor
|
|
112
|
-
.frame(height:
|
|
154
|
+
.frame(height: effectiveBottomInset)
|
|
113
155
|
}
|
|
114
156
|
.frame(maxWidth: .infinity)
|
|
115
|
-
.frame(
|
|
157
|
+
.frame(alignment: .bottom)
|
|
116
158
|
.background(
|
|
117
159
|
GeometryReader { geo in
|
|
118
160
|
Color.clear.onAppear { sheetHeight = geo.size.height }
|
|
@@ -143,13 +185,33 @@ struct BottomSheetView<Content: View>: View {
|
|
|
143
185
|
// MARK: - Drag handling (mirrors Compose's detectDragGestures)
|
|
144
186
|
|
|
145
187
|
private var dragGesture: some Gesture {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
188
|
+
// `.global` (not the default `.local`) so the gesture tracks against the
|
|
189
|
+
// window rather than the handle's own view, which is itself repositioned
|
|
190
|
+
// every frame by this same gesture's output (`sheet.offset(y: effectiveOffset)`
|
|
191
|
+
// in `body`) — the local coordinate space chasing its own moving reference
|
|
192
|
+
// frame is a plausible source of a continuous drag-time jitter.
|
|
193
|
+
DragGesture(coordinateSpace: .global)
|
|
194
|
+
.onChanged { _ in
|
|
195
|
+
// Touching the handle should immediately settle `sheetOffset` to its
|
|
196
|
+
// resting value (0) with no animation, so a drag that starts while the
|
|
197
|
+
// open animation — or the keyboard-driven resize, which shifts this
|
|
198
|
+
// same bottom-anchored top edge — is still in flight doesn't fight a
|
|
199
|
+
// moving target. Without this, the live drag value chases an animated
|
|
200
|
+
// one, producing a couple of corrective jumps before it settles.
|
|
201
|
+
if sheetOffset != 0 {
|
|
202
|
+
sheetOffset = 0
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
.updating($dragTranslation) { value, state, _ in
|
|
206
|
+
state = max(value.translation.height, 0)
|
|
150
207
|
}
|
|
151
|
-
.onEnded {
|
|
152
|
-
|
|
208
|
+
.onEnded { value in
|
|
209
|
+
let finalOffset = max(value.translation.height, 0)
|
|
210
|
+
// Capture into persisted state BEFORE @GestureState resets to 0,
|
|
211
|
+
// avoiding a one-frame snap back to rest before close()'s own
|
|
212
|
+
// slide-out animation starts (mirrors PopupView's `lastDragPosition`).
|
|
213
|
+
sheetOffset = finalOffset
|
|
214
|
+
if finalOffset > closeThreshold {
|
|
153
215
|
close()
|
|
154
216
|
} else {
|
|
155
217
|
withAnimation(.spring(response: 0.3, dampingFraction: 0.9)) {
|
|
@@ -163,17 +225,23 @@ struct BottomSheetView<Content: View>: View {
|
|
|
163
225
|
|
|
164
226
|
private func open() {
|
|
165
227
|
DispatchQueue.main.async {
|
|
166
|
-
withAnimation(.
|
|
228
|
+
withAnimation(.timingCurve(0.05, 0.7, 0.1, 1, duration: 0.1)) { backgroundAlpha = maxDynamicAlpha }
|
|
167
229
|
withAnimation(.timingCurve(0.05, 0.7, 0.1, 1, duration: 0.35)) { sheetOffset = 0 }
|
|
168
230
|
}
|
|
169
231
|
}
|
|
170
232
|
|
|
233
|
+
/// Mirrors Compose's `closeEvent`: slide out over 200ms, then fade the scrim
|
|
234
|
+
/// out over 100ms only after the slide finishes, then dismiss.
|
|
171
235
|
private func close() {
|
|
172
236
|
withAnimation(.timingCurve(0.3, 0, 0.8, 0.15, duration: 0.2)) {
|
|
173
237
|
sheetOffset = max(sheetHeight, screenHeight)
|
|
174
|
-
scrimAlpha = 0
|
|
175
238
|
}
|
|
176
239
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
240
|
+
withAnimation(.timingCurve(0.3, 0, 0.8, 0.15, duration: 0.1)) {
|
|
241
|
+
backgroundAlpha = 0
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
177
245
|
navigator.finishOverplayDismiss()
|
|
178
246
|
}
|
|
179
247
|
}
|
|
@@ -98,6 +98,7 @@ public struct StackScreen<Content: View>: View {
|
|
|
98
98
|
@State private var scrollOffset: CGFloat = 0
|
|
99
99
|
@State private var keyboardHeight: CGFloat = 0
|
|
100
100
|
@State private var headerRightWidth: CGFloat = 0
|
|
101
|
+
@State private var isBottomTabRoot: Bool = false
|
|
101
102
|
|
|
102
103
|
/// Header background fade tied to scroll: 0 → transparent (over a hero image
|
|
103
104
|
/// or transparent header), 1 → opaque surface with the drop shadow.
|
|
@@ -135,7 +136,10 @@ public struct StackScreen<Content: View>: View {
|
|
|
135
136
|
GeometryReader { geometry in
|
|
136
137
|
let keyboardSize: CGFloat = useAvoidKeyboard ? max(keyboardHeight, 0) : 0
|
|
137
138
|
let bottomInset = min(geometry.safeAreaInsets.bottom, 21)
|
|
138
|
-
|
|
139
|
+
// Skip this screen's own bottom-inset reservation when hosting a footer
|
|
140
|
+
// OR a self-contained BottomTab surface (mirrors Compose's
|
|
141
|
+
// `if (options.footerComponent != null || isBottomTab) 0.dp else navigationBar`).
|
|
142
|
+
let contentBottomInset = (footer == nil && !isBottomTabRoot) ? bottomInset : 0
|
|
139
143
|
|
|
140
144
|
ZStack(alignment: .top) {
|
|
141
145
|
// Background color
|
|
@@ -175,6 +179,9 @@ public struct StackScreen<Content: View>: View {
|
|
|
175
179
|
.onPreferenceChange(HeaderRightWidthKey.self) { width in
|
|
176
180
|
if headerRightWidth != width { headerRightWidth = width }
|
|
177
181
|
}
|
|
182
|
+
.onPreferenceChange(IsBottomTabRootKey.self) { isRoot in
|
|
183
|
+
if isBottomTabRoot != isRoot { isBottomTabRoot = isRoot }
|
|
184
|
+
}
|
|
178
185
|
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)) { notification in
|
|
179
186
|
guard useAvoidKeyboard,
|
|
180
187
|
let endFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
|
|
@@ -13,7 +13,7 @@ public enum Colors {
|
|
|
13
13
|
public static let textSecondary = Color.momoAsset("TextSecondary")
|
|
14
14
|
public static let card = Color.momoAsset("Card")
|
|
15
15
|
public static let error = Color.momoAsset("Error")
|
|
16
|
-
public static let info =
|
|
16
|
+
public static let info = blue03
|
|
17
17
|
public static let success = Color.momoAsset("Success")
|
|
18
18
|
|
|
19
19
|
public static let black20 = Color(hex: "000000")
|
|
@@ -49,15 +49,18 @@ public enum Colors {
|
|
|
49
49
|
public static let pink03 = Color(hex: "eb2f96")
|
|
50
50
|
public static let pink02 = Color(hex: "d42a87")
|
|
51
51
|
public static let pink01 = Color(hex: "bc2678")
|
|
52
|
+
public static let pinkMoMoBranding = Color(hex: "a50064")
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
public static let
|
|
55
|
-
public static let
|
|
55
|
+
public static let violet11 = Color(hex: "fcf8fe")
|
|
56
|
+
public static let violet11Stroke = Color(hex: "dfe1e5")
|
|
57
|
+
public static let violet10 = Color(hex: "faf4fe")
|
|
58
|
+
public static let violet09 = Color(hex: "f4e9fd")
|
|
56
59
|
public static let violet08 = Color(hex: "ead4fc")
|
|
57
60
|
public static let violet07 = Color(hex: "d5aaf9")
|
|
58
|
-
public static let violet06 = Color(hex: "
|
|
59
|
-
public static let violet05 = Color(hex: "
|
|
60
|
-
public static let violet04 = Color(hex: "
|
|
61
|
+
public static let violet06 = Color(hex: "c07ff6")
|
|
62
|
+
public static let violet05 = Color(hex: "ab55f3")
|
|
63
|
+
public static let violet04 = Color(hex: "a03ff1")
|
|
61
64
|
public static let violet03 = Color(hex: "962af0")
|
|
62
65
|
public static let violet02 = Color(hex: "8726d8")
|
|
63
66
|
public static let violet01 = Color(hex: "7822c0")
|
|
@@ -8,5 +8,7 @@ public enum Spacing {
|
|
|
8
8
|
public static let L: CGFloat = 16
|
|
9
9
|
public static let XL: CGFloat = 24
|
|
10
10
|
public static let XXL: CGFloat = 32
|
|
11
|
-
public static let
|
|
11
|
+
public static let Size3XL: CGFloat = 48
|
|
12
|
+
public static let Size4XL: CGFloat = 56
|
|
13
|
+
public static let Size5XL: CGFloat = 64
|
|
12
14
|
}
|
package/ios/native-kits.podspec
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# MoMoUIKitsDemo
|
|
2
|
+
|
|
3
|
+
SwiftUI demo browser for `MoMoUIKits`.
|
|
4
|
+
|
|
5
|
+
The package can optionally host a Compose UIKits screen by receiving a
|
|
6
|
+
`UIViewController` factory from the host app. It does not import the KMP
|
|
7
|
+
`shared` module directly.
|
|
8
|
+
|
|
9
|
+
SwiftUI mode reuses the full demo screen set from
|
|
10
|
+
`sample/iosApp/iosApp/Demo`, including the sample `HomeView` registry and demo
|
|
11
|
+
screens for all currently available components.
|
|
12
|
+
|
|
13
|
+
## CocoaPods
|
|
14
|
+
|
|
15
|
+
In the host app `Podfile`, include both local pods:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
pod 'MoMoUIKits', :path => '../momo-native-kits/ios/native-kits.podspec'
|
|
19
|
+
pod 'MoMoUIKitsDemo', :path => '../momo-native-kits/ios-demo'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Adjust the relative paths to match the host app checkout.
|
|
23
|
+
|
|
24
|
+
## Host App Usage
|
|
25
|
+
|
|
26
|
+
```swift
|
|
27
|
+
import MoMoUIKitsDemo
|
|
28
|
+
import shared
|
|
29
|
+
|
|
30
|
+
class UIKitsViewController: BaseComposeAppViewController {
|
|
31
|
+
override func getViewController() -> UIViewController? {
|
|
32
|
+
let params = self.application.data as? [String: Any]
|
|
33
|
+
|
|
34
|
+
return MoMoUIKitsDemoViewControllerFactory.make(
|
|
35
|
+
context: MoMoUIKitsDemoContext(
|
|
36
|
+
appId: params?["appId"] as? String ?? "",
|
|
37
|
+
appCode: params?["code"] as? String ?? "",
|
|
38
|
+
appName: params?["name"] as? String,
|
|
39
|
+
appIcon: params?["icon"] as? String ?? ""
|
|
40
|
+
),
|
|
41
|
+
composeViewController: {
|
|
42
|
+
UIKitsScreenComposeViewControllerKt.UIKitsScreenComposeViewController(
|
|
43
|
+
params: params
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
50
|
+
super.viewDidDisappear(animated)
|
|
51
|
+
composeViewController = nil
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If the host does not pass `composeViewController`, the demo opens directly in
|
|
57
|
+
SwiftUI mode.
|
|
58
|
+
|
|
59
|
+
## Included SwiftUI Demos
|
|
60
|
+
|
|
61
|
+
The pod includes the sample demo screens for:
|
|
62
|
+
|
|
63
|
+
- Animated Header
|
|
64
|
+
- Avatar
|
|
65
|
+
- Badge
|
|
66
|
+
- Baseline View
|
|
67
|
+
- Bottom Tab
|
|
68
|
+
- Button
|
|
69
|
+
- Carousel
|
|
70
|
+
- Checkbox
|
|
71
|
+
- Chip
|
|
72
|
+
- Collapse
|
|
73
|
+
- DateTimePicker
|
|
74
|
+
- Divider
|
|
75
|
+
- Header User
|
|
76
|
+
- Icon Button
|
|
77
|
+
- Image
|
|
78
|
+
- Information
|
|
79
|
+
- Input
|
|
80
|
+
- Input DropDown
|
|
81
|
+
- Input Money
|
|
82
|
+
- Input OTP
|
|
83
|
+
- Input Phone Number
|
|
84
|
+
- Input Search
|
|
85
|
+
- Input TextArea
|
|
86
|
+
- Loader
|
|
87
|
+
- Navigation
|
|
88
|
+
- Pagination
|
|
89
|
+
- Popup Display
|
|
90
|
+
- Popup Notify
|
|
91
|
+
- Popup Promotion
|
|
92
|
+
- Progress Info
|
|
93
|
+
- Radio
|
|
94
|
+
- Rating
|
|
95
|
+
- Skeleton
|
|
96
|
+
- Slider
|
|
97
|
+
- SnackBar
|
|
98
|
+
- Stepper
|
|
99
|
+
- Steps
|
|
100
|
+
- Suggest Action
|
|
101
|
+
- Swipe
|
|
102
|
+
- Switch
|
|
103
|
+
- TabView
|
|
104
|
+
- Tag
|
|
105
|
+
- Theme
|
|
106
|
+
- Title
|
|
107
|
+
- Tooltip
|
|
108
|
+
- Typography
|
|
109
|
+
- Uploader
|
|
110
|
+
|
|
111
|
+
## Validation Notes
|
|
112
|
+
|
|
113
|
+
Validated locally with:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
pod ipc spec ios/MoMoUIKits.podspec
|
|
117
|
+
pod ipc spec ios-demo/MoMoUIKitsDemo.podspec
|
|
118
|
+
pod lib lint ios/MoMoUIKits.podspec --allow-warnings --skip-import-validation
|
|
119
|
+
pod lib lint ios-demo/MoMoUIKitsDemo.podspec --allow-warnings --skip-import-validation --include-podspecs=ios/MoMoUIKits.podspec
|
|
120
|
+
```
|
|
@@ -89,6 +89,7 @@ public let ScreenUsages: [ScreenUsage] = [
|
|
|
89
89
|
ScreenUsage(title: "Bottom Tab", destination: AnyView(BottomTabDemo()), icon: getIcon(icon: "Layout")),
|
|
90
90
|
ScreenUsage(title: "Animated Header", destination: AnyView(AnimatedHeaderDemo()), icon: getIcon(icon: "Layout")),
|
|
91
91
|
ScreenUsage(title: "Theme", destination: AnyView(ThemeDemo()), icon: getIcon(icon: "Typography")),
|
|
92
|
+
ScreenUsage(title: "Localize", destination: AnyView(LocalizeDemo()), icon: getIcon(icon: "Typography")),
|
|
92
93
|
]
|
|
93
94
|
|
|
94
95
|
@available(iOS 16.0, *)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
//
|
|
2
|
+
// LocalizeDemo.swift
|
|
3
|
+
// MoMoUIKitsDemo
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI port of the Compose demo `sample/shared/.../screens/LocalizeUsage.kt`,
|
|
6
|
+
// showcasing the `Localize` i18n port (ios/Application/Localize.swift).
|
|
7
|
+
// Synced from sample/iosApp/iosApp/Demo/LocalizeDemo.swift.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import MoMoUIKits
|
|
11
|
+
import SwiftUI
|
|
12
|
+
|
|
13
|
+
// App-specific dictionary. Keys here do NOT exist in the kit defaults, so addTranslations
|
|
14
|
+
// registers them cleanly (addTranslations keeps existing keys on clash, host-default wins).
|
|
15
|
+
private let AppTranslations = LocalizationObject(
|
|
16
|
+
vi: [
|
|
17
|
+
"demo_greeting": "Xin chào MoMo!",
|
|
18
|
+
"demo_cart_title": "Giỏ hàng của bạn",
|
|
19
|
+
],
|
|
20
|
+
en: [
|
|
21
|
+
"demo_greeting": "Hello MoMo!",
|
|
22
|
+
"demo_cart_title": "Your cart",
|
|
23
|
+
]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
struct LocalizeDemo: View {
|
|
27
|
+
@EnvironmentObject private var localize: Localize
|
|
28
|
+
|
|
29
|
+
private var isEnglish: Bool { localize.currentLanguage == Localize.EN }
|
|
30
|
+
|
|
31
|
+
var body: some View {
|
|
32
|
+
ScrollView {
|
|
33
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
34
|
+
|
|
35
|
+
LocalizeSectionBox(title: "Đổi ngôn ngữ (toàn app)") {
|
|
36
|
+
MomoText(
|
|
37
|
+
"localize.changeLanguage(...) đổi ngôn ngữ cho cả NavigationContainer — mọi màn hình đang đọc translate() sẽ cập nhật theo.",
|
|
38
|
+
typography: .descriptionDefaultRegular
|
|
39
|
+
)
|
|
40
|
+
Switch(
|
|
41
|
+
.constant(isEnglish),
|
|
42
|
+
onChange: { _ in localize.changeLanguage(isEnglish ? Localize.VI : Localize.EN) },
|
|
43
|
+
title: "English"
|
|
44
|
+
)
|
|
45
|
+
MomoText("currentLanguage = \(localize.currentLanguage)", typography: .labelDefaultMedium)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
LocalizeSectionBox(title: "translate(key) — từ điển mặc định của kit") {
|
|
49
|
+
TranslationRow(key: "confirm", value: localize.translate("confirm"))
|
|
50
|
+
TranslationRow(key: "cancel", value: localize.translate("cancel"))
|
|
51
|
+
TranslationRow(key: "done", value: localize.translate("done"))
|
|
52
|
+
TranslationRow(key: "seeMore", value: localize.translate("seeMore"))
|
|
53
|
+
TranslationRow(key: "balance", value: localize.translate("balance"))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
LocalizeSectionBox(title: "Key thiếu → trả về chính key") {
|
|
57
|
+
MomoText(
|
|
58
|
+
"translate() với key không có (hoặc giá trị rỗng) sẽ trả lại đúng key để dễ phát hiện thiếu bản dịch.",
|
|
59
|
+
typography: .descriptionDefaultRegular
|
|
60
|
+
)
|
|
61
|
+
TranslationRow(key: "not_a_real_key", value: localize.translate("not_a_real_key"))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
LocalizeSectionBox(title: "Chèn tham số vào bản dịch") {
|
|
65
|
+
MomoText(
|
|
66
|
+
"Bản port không tự nội suy {placeholder} — tự replace ở nơi dùng.",
|
|
67
|
+
typography: .descriptionDefaultRegular
|
|
68
|
+
)
|
|
69
|
+
TranslationRow(
|
|
70
|
+
key: "voucherRemindHour",
|
|
71
|
+
value: localize.translate("voucherRemindHour").replacingOccurrences(of: "{hours}", with: "3")
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
LocalizeSectionBox(title: "translateData(vi:en:) — dịch nội tuyến") {
|
|
76
|
+
MomoText(
|
|
77
|
+
"Cho chuỗi dùng một lần, không cần khai báo key trong từ điển.",
|
|
78
|
+
typography: .descriptionDefaultRegular
|
|
79
|
+
)
|
|
80
|
+
MomoText(localize.translateData(vi: "Thanh toán ngay", en: "Pay now"), typography: .headerDefaultBold)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
LocalizeSectionBox(title: "translateData(map) — chọn theo ngôn ngữ") {
|
|
84
|
+
MomoText(
|
|
85
|
+
localize.translateData([
|
|
86
|
+
Localize.VI: "Lịch sử giao dịch",
|
|
87
|
+
Localize.EN: "Transaction history",
|
|
88
|
+
]),
|
|
89
|
+
typography: .headerDefaultBold
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
LocalizeSectionBox(title: "addTranslations — từ điển riêng của app") {
|
|
94
|
+
MomoText(
|
|
95
|
+
"Host đăng ký key riêng (đã add khi màn hình xuất hiện), rồi dùng translate() như key mặc định.",
|
|
96
|
+
typography: .descriptionDefaultRegular
|
|
97
|
+
)
|
|
98
|
+
TranslationRow(key: "demo_greeting", value: localize.translate("demo_greeting"))
|
|
99
|
+
TranslationRow(key: "demo_cart_title", value: localize.translate("demo_cart_title"))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
LocalizeSectionBox(title: "Ví dụ thực tế") {
|
|
103
|
+
// errorCode already ends with a separator ("Mã lỗi: " / "Error code: ").
|
|
104
|
+
MomoText(localize.translate("errorCode") + "E-4097", typography: .bodyDefaultRegular)
|
|
105
|
+
HStack(spacing: Spacing.M) {
|
|
106
|
+
Button(
|
|
107
|
+
title: localize.translate("cancel"),
|
|
108
|
+
action: {},
|
|
109
|
+
type: .outline,
|
|
110
|
+
size: .medium,
|
|
111
|
+
isFull: false
|
|
112
|
+
)
|
|
113
|
+
Button(
|
|
114
|
+
title: localize.translate("confirm"),
|
|
115
|
+
action: {},
|
|
116
|
+
type: .primary,
|
|
117
|
+
size: .medium,
|
|
118
|
+
isFull: false
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Spacer(minLength: 32)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Registers the app-specific dictionary once per appearance; addTranslations'
|
|
127
|
+
// merge is idempotent, so re-entry (e.g. re-navigating back to this screen) is safe.
|
|
128
|
+
.onAppear {
|
|
129
|
+
localize.addTranslations(AppTranslations)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// MARK: - LocalizeSectionBox
|
|
135
|
+
|
|
136
|
+
/// Titled rounded-card section container, mirroring Compose's `BasicColumnBox`
|
|
137
|
+
/// (title label above a `theme.colors.background.surface` card with `Radius.M` corners).
|
|
138
|
+
private struct LocalizeSectionBox<Content: View>: View {
|
|
139
|
+
@Environment(\.appTheme) private var theme
|
|
140
|
+
let title: String
|
|
141
|
+
@ViewBuilder let content: () -> Content
|
|
142
|
+
|
|
143
|
+
var body: some View {
|
|
144
|
+
VStack(alignment: .leading, spacing: 8) {
|
|
145
|
+
if !title.isEmpty {
|
|
146
|
+
MomoText(title, typography: .headerMBold)
|
|
147
|
+
.padding(.top, 8)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
VStack(alignment: .leading, spacing: 8) {
|
|
151
|
+
content()
|
|
152
|
+
}
|
|
153
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
154
|
+
.padding(12)
|
|
155
|
+
.background(theme.colors.background.surface)
|
|
156
|
+
.clipShape(RoundedRectangle(cornerRadius: Radius.M))
|
|
157
|
+
}
|
|
158
|
+
.padding(12)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// MARK: - TranslationRow
|
|
163
|
+
|
|
164
|
+
private struct TranslationRow: View {
|
|
165
|
+
@Environment(\.appTheme) private var theme
|
|
166
|
+
let key: String
|
|
167
|
+
let value: String
|
|
168
|
+
|
|
169
|
+
var body: some View {
|
|
170
|
+
VStack(alignment: .leading, spacing: 2) {
|
|
171
|
+
MomoText(key, typography: .labelDefaultMedium, color: theme.colors.text.secondary)
|
|
172
|
+
MomoText(value, typography: .bodyDefaultRegular)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// MARK: - Preview
|
|
178
|
+
|
|
179
|
+
#Preview {
|
|
180
|
+
if #available(iOS 16.0, *) {
|
|
181
|
+
NavigationStack {
|
|
182
|
+
LocalizeDemo()
|
|
183
|
+
.environmentObject(Localize())
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
// Fallback on earlier versions
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -23,6 +23,7 @@ struct NavigationDemo: View {
|
|
|
23
23
|
@available(iOS 16.0, *)
|
|
24
24
|
private struct NavigationDemoContent: View {
|
|
25
25
|
@EnvironmentObject private var navigator: Navigator
|
|
26
|
+
@State private var textValue1 = ""
|
|
26
27
|
|
|
27
28
|
var body: some View {
|
|
28
29
|
ScrollView {
|
|
@@ -72,6 +73,7 @@ private struct NavigationDemoContent: View {
|
|
|
72
73
|
row("Show Bottom Sheet") {
|
|
73
74
|
navigator.showBottomSheet({
|
|
74
75
|
VStack(spacing: 12) {
|
|
76
|
+
Input(text: $textValue1, placeholder: "Test Bottom Sheet")
|
|
75
77
|
ForEach(0..<4, id: \.self) { i in
|
|
76
78
|
MomoText("Bottom sheet row \(i + 1)")
|
|
77
79
|
.frame(maxWidth: .infinity, alignment: .leading)
|
package/local.properties
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## This file must *NOT* be checked into Version Control Systems,
|
|
2
|
+
# as it contains information specific to your local configuration.
|
|
3
|
+
#
|
|
4
|
+
# Location of the SDK. This is only used by Gradle.
|
|
5
|
+
# For customization when using a Version Control System, please read the
|
|
6
|
+
# header note.
|
|
7
|
+
#Wed Aug 21 14:20:12 ICT 2024
|
|
8
|
+
sdk.dir=/Users/huynhdung/Library/Android/sdk
|