@momo-kits/native-kits 0.163.1-sp.8 → 0.163.2-beta.1-debug
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codegraph/.gitignore +5 -0
- package/CLAUDE.md +0 -10
- 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 +150 -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 +293 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +112 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +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 +325 -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 +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +38 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +299 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +100 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +172 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +372 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +581 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +156 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +207 -0
- package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +32 -0
- package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +16 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +6 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +5 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +58 -0
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +60 -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/ApplicationEnvironment.swift +4 -6
- package/ios/Application/HeaderRight.swift +17 -1
- package/ios/Application/Localize.swift +4 -73
- package/ios/Application/Navigation/NavigationContainer.swift +18 -25
- package/ios/Application/Navigation/Navigator.swift +29 -91
- package/ios/Application/StackScreen.swift +21 -18
- package/ios/Badge/BadgeRibbon.swift +1 -4
- package/ios/Button/Button.swift +61 -41
- package/ios/Icon/Icon.swift +4 -13
- package/ios/native-kits.podspec +1 -2
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/README.md +120 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +8 -8
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +2 -3
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +12 -5
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +4 -51
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +2 -14
- package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +1 -3
- package/local.properties +8 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
- package/.claude/settings.local.json +0 -158
- package/ios/Application/LanguageSource.swift +0 -93
- package/ios/Application/LocalizeModifier.swift +0 -30
- package/ios/Application/Navigation/NavigatorManager.swift +0 -49
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Layout/GridContext.swift +0 -30
- package/ios/Layout/Item.swift +0 -42
- package/ios/Layout/Section.swift +0 -134
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +0 -104
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +0 -100
|
@@ -89,11 +89,7 @@ public struct OverplayItem: Identifiable {
|
|
|
89
89
|
|
|
90
90
|
@available(iOS 16.0, *)
|
|
91
91
|
public final class Navigator: ObservableObject {
|
|
92
|
-
@Published public var path: [DynamicScreenRoute] = []
|
|
93
|
-
// Disposal hangs off the path so every route that leaves the stack — pop,
|
|
94
|
-
// replace, reset — is released the same way, after its exit transition.
|
|
95
|
-
didSet { disposeScreens(droppedFrom: oldValue) }
|
|
96
|
-
}
|
|
92
|
+
@Published public var path: [DynamicScreenRoute] = []
|
|
97
93
|
@Published public var presented: DynamicDialogRoute?
|
|
98
94
|
@Published public var overplay: OverplayItem?
|
|
99
95
|
|
|
@@ -151,7 +147,8 @@ public final class Navigator: ObservableObject {
|
|
|
151
147
|
|
|
152
148
|
public func replace<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
|
|
153
149
|
guard !path.isEmpty else { return }
|
|
154
|
-
path.removeLast()
|
|
150
|
+
let removed = path.removeLast()
|
|
151
|
+
registry.unregister(id: removed.id)
|
|
155
152
|
push(content, options: options)
|
|
156
153
|
}
|
|
157
154
|
|
|
@@ -161,12 +158,14 @@ public final class Navigator: ObservableObject {
|
|
|
161
158
|
options: NavigationOptions? = nil
|
|
162
159
|
) {
|
|
163
160
|
guard !path.isEmpty else { return }
|
|
164
|
-
path.removeLast()
|
|
161
|
+
let removed = path.removeLast()
|
|
162
|
+
registry.unregister(id: removed.id)
|
|
165
163
|
push(screenName: screenName, content, options: options)
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
public func reset<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
|
|
169
|
-
|
|
167
|
+
for route in path { registry.unregister(id: route.id) }
|
|
168
|
+
registry.unregister(id: rootRoute.id)
|
|
170
169
|
path.removeAll()
|
|
171
170
|
let route = registry.register(
|
|
172
171
|
screenName: options?.screenName ?? "",
|
|
@@ -174,7 +173,6 @@ public final class Navigator: ObservableObject {
|
|
|
174
173
|
options: options
|
|
175
174
|
)
|
|
176
175
|
rootRoute = route
|
|
177
|
-
disposeScreens([previousRoot])
|
|
178
176
|
}
|
|
179
177
|
|
|
180
178
|
public func reset<V: View>(
|
|
@@ -182,7 +180,8 @@ public final class Navigator: ObservableObject {
|
|
|
182
180
|
@ViewBuilder _ content: @escaping () -> V,
|
|
183
181
|
options: NavigationOptions? = nil
|
|
184
182
|
) {
|
|
185
|
-
|
|
183
|
+
for route in path { registry.unregister(id: route.id) }
|
|
184
|
+
registry.unregister(id: rootRoute.id)
|
|
186
185
|
path.removeAll()
|
|
187
186
|
let route = registry.register(
|
|
188
187
|
screenName: screenName,
|
|
@@ -190,50 +189,26 @@ public final class Navigator: ObservableObject {
|
|
|
190
189
|
options: options
|
|
191
190
|
)
|
|
192
191
|
rootRoute = route
|
|
193
|
-
disposeScreens([previousRoot])
|
|
194
192
|
}
|
|
195
193
|
|
|
196
194
|
public func pop(_ count: Int = 1, callback: (() -> Void)? = nil) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if overplay?.type == .snackBar {
|
|
211
|
-
// Mirrors Kotlin: a snackbar doesn't block back-navigation, so popping
|
|
212
|
-
// while one is showing pops the screen underneath and clears the
|
|
213
|
-
// snackbar immediately, without playing its dismiss animation.
|
|
214
|
-
overplayDismissHandler = nil
|
|
215
|
-
overplay = nil
|
|
216
|
-
guard popScreen() else {
|
|
217
|
-
callback?()
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
popStep(remaining: remaining - 1, callback: callback)
|
|
221
|
-
} else if overplay != nil {
|
|
222
|
-
hideOverplay()
|
|
223
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + Self.overplayDismissDelay) { [weak self] in
|
|
224
|
-
guard let self else { return }
|
|
225
|
-
// The component reports back through `finishOverplayDismiss` once its exit
|
|
226
|
-
// animation ends; clear it here if it never did, so the step is not wasted.
|
|
227
|
-
if self.overplay != nil { self.finishOverplayDismiss() }
|
|
228
|
-
self.popStep(remaining: remaining - 1, callback: callback)
|
|
195
|
+
var remaining = count
|
|
196
|
+
while remaining > 0 {
|
|
197
|
+
if overplay?.type == .snackBar {
|
|
198
|
+
// Mirrors Kotlin: a snackbar doesn't block back-navigation, so popping
|
|
199
|
+
// while one is showing pops the screen underneath and clears the
|
|
200
|
+
// snackbar immediately, without playing its dismiss animation.
|
|
201
|
+
overplayDismissHandler = nil
|
|
202
|
+
overplay = nil
|
|
203
|
+
if !popScreen() { break }
|
|
204
|
+
} else if overplay != nil {
|
|
205
|
+
hideOverplay()
|
|
206
|
+
} else if !popScreen() {
|
|
207
|
+
break
|
|
229
208
|
}
|
|
230
|
-
|
|
231
|
-
guard popScreen() else {
|
|
232
|
-
callback?()
|
|
233
|
-
return
|
|
234
|
-
}
|
|
235
|
-
popStep(remaining: remaining - 1, callback: callback)
|
|
209
|
+
remaining -= 1
|
|
236
210
|
}
|
|
211
|
+
callback?()
|
|
237
212
|
}
|
|
238
213
|
|
|
239
214
|
/// Pops the topmost presented screen or stack entry. Returns `false` (and requests
|
|
@@ -243,10 +218,11 @@ public final class Navigator: ObservableObject {
|
|
|
243
218
|
if presented != nil {
|
|
244
219
|
let dismissedId = presented?.id
|
|
245
220
|
presented = nil
|
|
246
|
-
if let id = dismissedId {
|
|
221
|
+
if let id = dismissedId { registry.unregister(id: id) }
|
|
247
222
|
return true
|
|
248
223
|
} else if !path.isEmpty {
|
|
249
|
-
path.removeLast()
|
|
224
|
+
let removed = path.removeLast()
|
|
225
|
+
registry.unregister(id: removed.id)
|
|
250
226
|
return true
|
|
251
227
|
} else {
|
|
252
228
|
composeApi?.requestCallback(funcName: "dismiss", params: nil, onResponse: nil)
|
|
@@ -255,18 +231,12 @@ public final class Navigator: ObservableObject {
|
|
|
255
231
|
}
|
|
256
232
|
|
|
257
233
|
public func popToRoot() {
|
|
234
|
+
for route in path { registry.unregister(id: route.id) }
|
|
258
235
|
path.removeAll()
|
|
259
236
|
}
|
|
260
237
|
|
|
261
|
-
/// The entry a back action applies to — mirrors Compose's `currentScreen()`
|
|
262
|
-
/// (`navController.currentBackStackEntry`): a presented screen sits on top of
|
|
263
|
-
/// the stack, otherwise it is the last pushed screen, otherwise the root.
|
|
264
|
-
private func currentScreen() -> DynamicScreen? {
|
|
265
|
-
registry.getScreen(id: presented?.id ?? path.last?.id ?? rootRoute.id)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
238
|
public func onBackSafe(callback: (() -> Void)? = nil) {
|
|
269
|
-
let latest =
|
|
239
|
+
let latest = registry.latest()
|
|
270
240
|
if let onBack = latest?.options?.onBackHandler {
|
|
271
241
|
onBack()
|
|
272
242
|
return
|
|
@@ -412,36 +382,4 @@ public final class Navigator: ObservableObject {
|
|
|
412
382
|
public func hideSnackBar() {
|
|
413
383
|
if overplay?.type == .snackBar { hideOverplay() }
|
|
414
384
|
}
|
|
415
|
-
|
|
416
|
-
// MARK: Screen disposal
|
|
417
|
-
|
|
418
|
-
/// Matches the `delay(300)` Compose waits in `dismissScreen` before unregistering,
|
|
419
|
-
/// with a little headroom for the pop transition.
|
|
420
|
-
private static let screenDisposeDelay: TimeInterval = 0.35
|
|
421
|
-
|
|
422
|
-
/// Longest overplay exit animation (the bottom sheet's), so a queued pop step runs
|
|
423
|
-
/// after the component has taken itself down.
|
|
424
|
-
private static let overplayDismissDelay: TimeInterval = 0.3
|
|
425
|
-
|
|
426
|
-
private func disposeScreens(droppedFrom previous: [DynamicScreenRoute]) {
|
|
427
|
-
let live = Set(path.map(\.id))
|
|
428
|
-
disposeScreens(previous.map(\.id).filter { !live.contains($0) })
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/// Screens are released only once the pop transition has played out: the outgoing
|
|
432
|
-
/// view is still on screen while it animates and the registry is what renders it —
|
|
433
|
-
/// dropping it right away blanks the screen mid-transition. Liveness is re-checked
|
|
434
|
-
/// when the timer fires, so a route pushed back before then keeps its screen.
|
|
435
|
-
private func disposeScreens(_ ids: [Int]) {
|
|
436
|
-
guard !ids.isEmpty else { return }
|
|
437
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + Self.screenDisposeDelay) { [weak self] in
|
|
438
|
-
guard let self else { return }
|
|
439
|
-
var live = Set(self.path.map(\.id))
|
|
440
|
-
live.insert(self.rootRoute.id)
|
|
441
|
-
if let presented = self.presented { live.insert(presented.id) }
|
|
442
|
-
for id in ids where !live.contains(id) {
|
|
443
|
-
self.registry.unregister(id: id)
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
385
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// SwiftUI mirror of Compose's `StackScreen`. This is a refactor of `Screen`:
|
|
6
6
|
// the body is decomposed into the same focused pieces Compose uses — header
|
|
7
7
|
// layer, scrollable content (`StackScreenContent`), floating button
|
|
8
|
-
// (`FloatingContent`) and footer (`
|
|
8
|
+
// (`FloatingContent`) and footer (`FooterContent`) — instead of one
|
|
9
9
|
// monolithic, partially-duplicated view. The public init matches `Screen`
|
|
10
10
|
// so call sites (e.g. NavigationContainer's ScreenHost) can swap over.
|
|
11
11
|
//
|
|
@@ -165,13 +165,12 @@ public struct StackScreen<Content: View>: View {
|
|
|
165
165
|
|
|
166
166
|
FloatingContent(
|
|
167
167
|
fabProps: fabProps,
|
|
168
|
-
|
|
169
|
-
hasFooter: footer != nil,
|
|
168
|
+
keyboardOffset: 0,
|
|
170
169
|
scrollOffset: scrollOffset
|
|
171
170
|
)
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
|
|
173
|
+
FooterContent(footer: footer, keyboardSize: keyboardSize, bottomInset: bottomInset)
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
// Animated search bar overlay (mirrors Compose's SearchAnimated layer).
|
|
@@ -445,29 +444,17 @@ private struct SearchAnimatedHeader: View {
|
|
|
445
444
|
/// Mirrors Compose's `FloatingContent`: no FAB props means nothing renders.
|
|
446
445
|
private struct FloatingContent: View {
|
|
447
446
|
let fabProps: FabProps?
|
|
448
|
-
let
|
|
449
|
-
/// With a footer the FAB is anchored to the footer's *layout* top, which does not move
|
|
450
|
-
/// when the band offsets itself over the keyboard — so it rides the same offset.
|
|
451
|
-
let hasFooter: Bool
|
|
447
|
+
let keyboardOffset: CGFloat
|
|
452
448
|
let scrollOffset: CGFloat
|
|
453
449
|
|
|
454
|
-
/// Compose keeps the FAB `Spacing.M + AppNavigationBar` above the bottom of the screen
|
|
455
|
-
/// and lifts it with `imePadding()`; here it sits `Spacing.M` above the safe area and
|
|
456
|
-
/// nothing lifts it, so the difference is applied as an offset (see NavigationFooter).
|
|
457
|
-
private var verticalOffset: CGFloat {
|
|
458
|
-
if hasFooter { return NavigationFooter.verticalOffset(keyboardSize: keyboardSize) }
|
|
459
|
-
return NavigationFooter.safeAreaBottom - NavigationFooter.navigationBarInset - keyboardSize
|
|
460
|
-
}
|
|
461
|
-
|
|
462
450
|
var body: some View {
|
|
463
451
|
if let fabProps = fabProps {
|
|
464
452
|
FloatingButton(
|
|
465
453
|
props: fabProps,
|
|
466
|
-
keyboardOffset:
|
|
454
|
+
keyboardOffset: keyboardOffset,
|
|
467
455
|
scrollOffset: scrollOffset
|
|
468
456
|
)
|
|
469
457
|
.padding(.horizontal, Spacing.M)
|
|
470
|
-
.offset(y: verticalOffset)
|
|
471
458
|
.ignoresSafeArea(.keyboard, edges: .bottom)
|
|
472
459
|
}
|
|
473
460
|
}
|
|
@@ -496,3 +483,19 @@ private struct HideKeyboardOnTap: ViewModifier {
|
|
|
496
483
|
}
|
|
497
484
|
}
|
|
498
485
|
}
|
|
486
|
+
|
|
487
|
+
// MARK: - Footer content
|
|
488
|
+
|
|
489
|
+
/// Mirrors Compose's `FooterContent`: renders the footer chrome only when a
|
|
490
|
+
/// footer component is supplied. Reuses the existing `Footer` view from Screen.swift.
|
|
491
|
+
private struct FooterContent: View {
|
|
492
|
+
let footer: (() -> AnyView)?
|
|
493
|
+
let keyboardSize: CGFloat
|
|
494
|
+
let bottomInset: CGFloat
|
|
495
|
+
|
|
496
|
+
var body: some View {
|
|
497
|
+
if let footerView = footer?() {
|
|
498
|
+
Footer(footerView: footerView, keyboardSize: keyboardSize, bottomInset: bottomInset)
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
@@ -174,7 +174,6 @@ public struct BadgeRibbon: View {
|
|
|
174
174
|
)
|
|
175
175
|
.frame(width: RibbonConstants.headTailWidth, height: RibbonConstants.headTailHeight)
|
|
176
176
|
.rotationEffect(.degrees(180))
|
|
177
|
-
.offset(x: 1)
|
|
178
177
|
.clipped()
|
|
179
178
|
}
|
|
180
179
|
|
|
@@ -184,7 +183,6 @@ public struct BadgeRibbon: View {
|
|
|
184
183
|
placeholder: AnyView(Color.clear)
|
|
185
184
|
)
|
|
186
185
|
.frame(width: RibbonConstants.headTailWidth, height: RibbonConstants.headTailHeight)
|
|
187
|
-
.offset(x: 1)
|
|
188
186
|
.clipped()
|
|
189
187
|
}
|
|
190
188
|
|
|
@@ -194,7 +192,6 @@ public struct BadgeRibbon: View {
|
|
|
194
192
|
placeholder: AnyView(Color.clear)
|
|
195
193
|
)
|
|
196
194
|
.frame(width: RibbonConstants.skewTailWidth, height: RibbonConstants.skewTailHeight)
|
|
197
|
-
.offset(x: -2)
|
|
198
195
|
.clipped()
|
|
199
196
|
}
|
|
200
197
|
}
|
|
@@ -284,7 +281,7 @@ private struct RibbonConstants {
|
|
|
284
281
|
static let ribbonHeight: CGFloat = 20
|
|
285
282
|
static let roundHeight: CGFloat = 16
|
|
286
283
|
static let skewBodyHeight: CGFloat = 16
|
|
287
|
-
static let roundRightRadius: CGFloat =
|
|
284
|
+
static let roundRightRadius: CGFloat = 12
|
|
288
285
|
static let roundPaddingEnd: CGFloat = 6
|
|
289
286
|
static let skewTailWidth: CGFloat = 8
|
|
290
287
|
static let skewTailHeight: CGFloat = 16
|
package/ios/Button/Button.swift
CHANGED
|
@@ -133,19 +133,19 @@ public struct Button: View {
|
|
|
133
133
|
var action: () -> Void
|
|
134
134
|
var type: ButtonType
|
|
135
135
|
var size: ButtonSize
|
|
136
|
-
var iconLeft:
|
|
137
|
-
var iconRight:
|
|
136
|
+
var iconLeft: AnyView?
|
|
137
|
+
var iconRight: AnyView?
|
|
138
138
|
var isFull: Bool
|
|
139
139
|
var loading: Bool
|
|
140
140
|
var useTintColor: Bool
|
|
141
141
|
|
|
142
142
|
public init(
|
|
143
|
-
title: String = "
|
|
143
|
+
title: String = "",
|
|
144
144
|
action: @escaping () -> Void,
|
|
145
145
|
type: ButtonType = .primary,
|
|
146
146
|
size: ButtonSize = .large,
|
|
147
|
-
iconLeft:
|
|
148
|
-
iconRight:
|
|
147
|
+
iconLeft: AnyView? = nil,
|
|
148
|
+
iconRight: AnyView? = nil,
|
|
149
149
|
isFull: Bool = true,
|
|
150
150
|
loading: Bool = false,
|
|
151
151
|
useTintColor: Bool = true
|
|
@@ -161,9 +161,38 @@ public struct Button: View {
|
|
|
161
161
|
self.useTintColor = useTintColor
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
/// Mirrors Kotlin's `Button(iconLeft: String, iconRight: String, ...)`, which resolves
|
|
165
|
+
/// icons by design-system source name through the shared `Icon` component rather than
|
|
166
|
+
/// a caller-supplied view. `iconLeft` is required here (unlike Kotlin's `= ""` default)
|
|
167
|
+
/// solely to keep this initializer unambiguous alongside the `AnyView`-based one above —
|
|
168
|
+
/// pass `""` explicitly for an icon-right-only button.
|
|
169
|
+
public init(
|
|
170
|
+
title: String = "Button",
|
|
171
|
+
action: @escaping () -> Void,
|
|
172
|
+
type: ButtonType = .primary,
|
|
173
|
+
size: ButtonSize = .large,
|
|
174
|
+
iconLeft: String,
|
|
175
|
+
iconRight: String = "",
|
|
176
|
+
isFull: Bool = true,
|
|
177
|
+
loading: Bool = false,
|
|
178
|
+
useTintColor: Bool = true
|
|
179
|
+
) {
|
|
180
|
+
self.init(
|
|
181
|
+
title: title,
|
|
182
|
+
action: action,
|
|
183
|
+
type: type,
|
|
184
|
+
size: size,
|
|
185
|
+
iconLeft: iconLeft.isEmpty ? nil : AnyView(Icon(source: iconLeft)),
|
|
186
|
+
iconRight: iconRight.isEmpty ? nil : AnyView(Icon(source: iconRight)),
|
|
187
|
+
isFull: isFull,
|
|
188
|
+
loading: loading,
|
|
189
|
+
useTintColor: useTintColor
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private func shouldLoadingOnLeft(_ left: AnyView?, _ right: AnyView?) -> Bool {
|
|
194
|
+
let hasLeft = left != nil
|
|
195
|
+
let hasRight = right != nil
|
|
167
196
|
|
|
168
197
|
switch (hasLeft, hasRight) {
|
|
169
198
|
case (false, false): return true
|
|
@@ -188,13 +217,27 @@ public struct Button: View {
|
|
|
188
217
|
}
|
|
189
218
|
}) {
|
|
190
219
|
HStack(spacing: size.iconSpacing) {
|
|
191
|
-
|
|
220
|
+
if loading && loadingOnLeft {
|
|
221
|
+
LottieView(name: "lottie_circle_loader", loopMode: .loop)
|
|
222
|
+
.frame(width: size.iconSize, height: size.iconSize)
|
|
223
|
+
.colorMultiply(fg)
|
|
224
|
+
|
|
225
|
+
} else if let iconLeft = iconLeft {
|
|
226
|
+
tintedIcon(iconLeft, color: fg)
|
|
227
|
+
}
|
|
192
228
|
|
|
193
229
|
MomoText(title, typography: size.typographyStyle, color: fg)
|
|
194
230
|
.lineLimit(1)
|
|
195
231
|
.truncationMode(.tail)
|
|
196
232
|
|
|
197
|
-
|
|
233
|
+
if loading && !loadingOnLeft {
|
|
234
|
+
LottieView(name: "lottie_circle_loader", loopMode: .loop)
|
|
235
|
+
.frame(width: size.iconSize, height: size.iconSize)
|
|
236
|
+
.colorMultiply(fg)
|
|
237
|
+
|
|
238
|
+
} else if let iconRight = iconRight {
|
|
239
|
+
tintedIcon(iconRight, color: fg)
|
|
240
|
+
}
|
|
198
241
|
}
|
|
199
242
|
.frame(maxWidth: isFull ? .infinity : nil)
|
|
200
243
|
.padding(.horizontal, size.padding)
|
|
@@ -206,24 +249,20 @@ public struct Button: View {
|
|
|
206
249
|
border: border,
|
|
207
250
|
borderWidth: borderWidth,
|
|
208
251
|
radius: size.radius,
|
|
209
|
-
height: size.height
|
|
210
|
-
isFull: isFull
|
|
252
|
+
height: size.height
|
|
211
253
|
))
|
|
212
254
|
.disabled(!isEnabled)
|
|
213
255
|
}
|
|
214
256
|
|
|
215
|
-
/// Mirrors Kotlin `RenderIcon`: the loader replaces the icon slot entirely, and
|
|
216
|
-
/// `useTintColor == false` leaves the icon at its own colors.
|
|
217
257
|
@ViewBuilder
|
|
218
|
-
private func
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
258
|
+
private func tintedIcon(_ icon: AnyView, color: Color) -> some View {
|
|
259
|
+
if useTintColor {
|
|
260
|
+
icon
|
|
261
|
+
.frame(width: size.iconSize, height: size.iconSize)
|
|
262
|
+
.colorMultiply(color)
|
|
263
|
+
} else {
|
|
264
|
+
icon
|
|
223
265
|
.frame(width: size.iconSize, height: size.iconSize)
|
|
224
|
-
.colorMultiply(color ?? Colors.black01)
|
|
225
|
-
} else if !icon.isEmpty {
|
|
226
|
-
Icon(source: icon, size: size.iconSize, color: color)
|
|
227
266
|
}
|
|
228
267
|
}
|
|
229
268
|
}
|
|
@@ -238,13 +277,11 @@ private struct ButtonPressFeedbackStyle: ButtonStyle {
|
|
|
238
277
|
let borderWidth: CGFloat
|
|
239
278
|
let radius: CGFloat
|
|
240
279
|
let height: CGFloat
|
|
241
|
-
let isFull: Bool
|
|
242
280
|
|
|
243
281
|
func makeBody(configuration: Configuration) -> some View {
|
|
244
282
|
let pressed = configuration.isPressed && isEnabled
|
|
245
283
|
|
|
246
284
|
configuration.label
|
|
247
|
-
.modifier(HugContentOnLegacyOS(isFull: isFull))
|
|
248
285
|
.opacity(pressed ? 0.5 : 1)
|
|
249
286
|
.background(
|
|
250
287
|
RoundedRectangle(cornerRadius: radius)
|
|
@@ -260,20 +297,3 @@ private struct ButtonPressFeedbackStyle: ButtonStyle {
|
|
|
260
297
|
.animation(.easeInOut(duration: 0.1), value: pressed)
|
|
261
298
|
}
|
|
262
299
|
}
|
|
263
|
-
|
|
264
|
-
/// `ButtonStyleConfiguration.Label` is greedy on iOS 16 and earlier — it accepts
|
|
265
|
-
/// the full proposed width, so the background drawn in `makeBody` stretches past
|
|
266
|
-
/// the content even when `isFull == false`. iOS 17 fixed the label to hug its
|
|
267
|
-
/// content, so only legacy OSes need the `fixedSize` clamp (kept off on 17+ to
|
|
268
|
-
/// preserve tail truncation of long titles).
|
|
269
|
-
private struct HugContentOnLegacyOS: ViewModifier {
|
|
270
|
-
let isFull: Bool
|
|
271
|
-
|
|
272
|
-
func body(content: Content) -> some View {
|
|
273
|
-
if #available(iOS 17.0, *) {
|
|
274
|
-
content
|
|
275
|
-
} else {
|
|
276
|
-
content.fixedSize(horizontal: !isFull, vertical: false)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
package/ios/Icon/Icon.swift
CHANGED
|
@@ -26,24 +26,17 @@ let iconSources: [String: IconSource] = loadIconSources()
|
|
|
26
26
|
// Icons that must never be tinted (mirror Compose `noThemeIcons`)
|
|
27
27
|
let noThemeIcons: [String] = ["ic_round_momo_tiny"]
|
|
28
28
|
|
|
29
|
-
// Compose defaults `color` to the theme text color while `color = null` means "no tint".
|
|
30
|
-
// Swift can't tell an omitted argument from an explicit `nil`, so the default is this
|
|
31
|
-
// unrepresentable sentinel: omitted -> theme text color, `nil` -> untinted.
|
|
32
|
-
public let iconThemeColor = Color(.sRGB, red: -1, green: -1, blue: -1, opacity: -1)
|
|
33
|
-
|
|
34
29
|
// Icon ViewModel
|
|
35
30
|
public struct Icon: View {
|
|
36
|
-
@Environment(\.appTheme) private var theme
|
|
37
|
-
|
|
38
31
|
var source: String
|
|
39
32
|
var size: CGFloat
|
|
40
33
|
var color: Color?
|
|
41
34
|
var accessibilityLabel: String?
|
|
42
|
-
|
|
35
|
+
|
|
43
36
|
public init(
|
|
44
37
|
source: String,
|
|
45
38
|
size: CGFloat = 24,
|
|
46
|
-
color: Color? =
|
|
39
|
+
color: Color? = nil,
|
|
47
40
|
accessibilityLabel: String? = nil
|
|
48
41
|
) {
|
|
49
42
|
self.source = source
|
|
@@ -53,11 +46,9 @@ public struct Icon: View {
|
|
|
53
46
|
}
|
|
54
47
|
|
|
55
48
|
public var body: some View {
|
|
56
|
-
|
|
57
|
-
if let uri = source.contains("https") ? source : iconSources[source]?.uri {
|
|
58
|
-
let requestedColor = color == iconThemeColor ? theme.colors.text.default : color
|
|
49
|
+
if let uri = iconSources[source]?.uri {
|
|
59
50
|
// Mirror Compose: certain icons (e.g. branded glyphs) are never tinted
|
|
60
|
-
let resolvedColor = noThemeIcons.contains(source) ? nil :
|
|
51
|
+
let resolvedColor = noThemeIcons.contains(source) ? nil : color
|
|
61
52
|
ImageView(uri, color: resolvedColor)
|
|
62
53
|
.frame(width: size, height: size)
|
|
63
54
|
// Mirrors Compose's auto-generated `contentDescription = "img|$iconUrl"`
|
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.163.
|
|
3
|
+
s.version = '0.163.2-beta.1'
|
|
4
4
|
s.summary = 'MoMoUIKits for iOS'
|
|
5
5
|
s.homepage = 'https://momo.vn'
|
|
6
6
|
s.license = { :type => 'MIT' }
|
|
@@ -34,7 +34,6 @@ Pod::Spec.new do |s|
|
|
|
34
34
|
"InputDropDown/**/*.{swift,m,h}",
|
|
35
35
|
"InputMoney/**/*.{swift,m,h}",
|
|
36
36
|
"InputOTP/**/*.{swift,m,h}",
|
|
37
|
-
"Layout/**/*.{swift,m,h}",
|
|
38
37
|
"Loader/**/*.{swift,m,h}",
|
|
39
38
|
"Lottie/**/*.{swift,m,h}",
|
|
40
39
|
"OTPKeyboard/**/*.{swift,m,h}",
|
|
@@ -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
|
+
```
|