@momo-kits/native-kits 0.163.1-beta.8 → 0.163.1-beta.9-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 +1 -1
- 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 +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 +65 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +51 -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 +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 +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 +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 +128 -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/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 +15 -2
- package/ios/Application/Components.swift +50 -44
- package/ios/Application/FloatingButton.swift +10 -9
- package/ios/Application/HeaderRight.swift +79 -45
- package/ios/Application/Localize.swift +277 -0
- package/ios/Application/MaxApi.swift +18 -0
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +104 -0
- package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
- package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
- package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
- package/ios/Application/Navigation/HeaderTitle.swift +57 -0
- package/ios/Application/Navigation/HeaderUser.swift +209 -0
- package/ios/Application/Navigation/NavigationContainer.swift +200 -0
- package/ios/Application/Navigation/NavigationOptions.swift +109 -0
- package/ios/Application/Navigation/Navigator.swift +385 -0
- package/ios/Application/Navigation/Overplay/BottomSheet.swift +256 -0
- package/ios/Application/Navigation/Overplay/ModalScreen.swift +74 -0
- package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
- package/ios/Application/Navigation/Tracking/ScreenTracker.swift +117 -0
- package/ios/Application/Navigation/component/Header.swift +178 -0
- package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
- package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
- package/ios/Application/Screen.swift +2 -1
- package/ios/Application/StackScreen.swift +501 -0
- package/ios/Avatar/Avatar.swift +193 -0
- package/ios/Badge/Badge.swift +11 -14
- package/ios/Badge/BadgeRibbon.swift +92 -21
- package/ios/BaselineView/BaselineView.swift +163 -0
- package/ios/Button/Button.swift +124 -29
- package/ios/Carousel/Carousel.swift +196 -0
- package/ios/Checkbox/Checkbox.swift +23 -28
- package/ios/Chip/Chip.swift +56 -33
- package/ios/Collapse/Collapse.swift +192 -0
- package/ios/Colors+Radius+Spacing/Colors.swift +20 -17
- package/ios/Colors+Radius+Spacing/Spacing.swift +3 -1
- package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
- package/ios/DateTimePicker/DateTimePicker.swift +212 -0
- package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
- package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
- package/ios/DateTimePicker/WheelPicker.swift +165 -0
- package/ios/Divider/Divider.swift +16 -0
- package/ios/Extensions/ActiveOpacityButtonStyle.swift +14 -0
- package/ios/Extensions/Color++.swift +32 -0
- package/ios/Icon/Icon.swift +10 -1
- package/ios/IconButton/IconButton.swift +140 -0
- package/ios/Image/Image.swift +16 -3
- package/ios/Information/Information.swift +3 -1
- package/ios/Input/ErrorView.swift +3 -1
- package/ios/Input/Input.swift +72 -33
- package/ios/Input/InputPhoneNumber.swift +33 -21
- package/ios/Input/InputSearch.swift +107 -48
- package/ios/Input/InputTextArea.swift +134 -64
- package/ios/InputDropDown/InputDropDown.swift +201 -0
- package/ios/InputMoney/InputMoney.swift +347 -0
- package/ios/InputOTP/InputOTP.swift +210 -0
- package/ios/Loader/Loader.swift +113 -0
- package/ios/MoMoUIKits.podspec +5 -0
- package/ios/Pagination/PaginationDot.swift +61 -0
- package/ios/Pagination/PaginationNumber.swift +35 -0
- package/ios/Pagination/PaginationScroll.swift +101 -0
- package/ios/Pagination/PaginationWhiteDot.swift +37 -0
- package/ios/Popup/PopupDisplay.swift +11 -20
- package/ios/Popup/PopupInput.swift +2 -8
- package/ios/Popup/PopupNotify.swift +218 -0
- package/ios/Popup/PopupPromotion.swift +16 -8
- package/ios/ProgressInfo/ProgressInfo.swift +296 -0
- package/ios/Radio/Radio.swift +69 -0
- package/ios/Rating/Rating.swift +82 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +6 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
- package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +38 -0
- package/ios/Skeleton/Skeleton.swift +99 -0
- package/ios/Slider/Slider.swift +237 -0
- package/ios/Stepper/Stepper.swift +220 -0
- package/ios/Steps/Steps.swift +467 -0
- package/ios/SuggestAction/SuggestAction.swift +99 -0
- package/ios/Swipeable/SwipeCell.swift +187 -0
- package/ios/Swipeable/SwipeCellModel.swift +21 -0
- package/ios/Switch/Switch.swift +53 -28
- package/ios/TabView/TabView.swift +534 -0
- package/ios/Tag/Tag.swift +98 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +6 -7
- package/ios/Title/Title.swift +269 -0
- package/ios/Tooltip/Tooltip.swift +485 -0
- package/ios/Typography/FontScaleStore.swift +19 -0
- package/ios/Typography/Text.swift +79 -5
- package/ios/Typography/Typography.swift +3 -0
- package/ios/Uploader/Uploader.swift +174 -0
- package/ios/native-kits.podspec +61 -8
- package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
- package/ios-demo/README.md +120 -0
- package/ios-demo/Resources/dot_loading.json +558 -0
- package/ios-demo/Resources/icon.json +4052 -0
- package/ios-demo/Resources/lottie_circle_loader.json +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +63 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +279 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +188 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +229 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +51 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
- package/local.properties +8 -0
- package/package.json +1 -1
- package/publish.sh +53 -0
- package/scripts/gen-ios-color.mjs +136 -0
- package/settings.gradle.kts +64 -0
- package/.claude/settings.local.json +0 -62
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BottomSheet.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `BottomSheet` (navigation/BottomSheet.kt).
|
|
6
|
+
// A sheet that slides up from the bottom over a dimmed scrim, with a drag
|
|
7
|
+
// handle, a header (title or custom), a divider and the content body.
|
|
8
|
+
// Dragging down past a threshold (or tapping the scrim) dismisses it.
|
|
9
|
+
// Driven by `Navigator.showBottomSheet`.
|
|
10
|
+
//
|
|
11
|
+
|
|
12
|
+
import SwiftUI
|
|
13
|
+
|
|
14
|
+
// MARK: - Header model
|
|
15
|
+
|
|
16
|
+
/// Mirrors Compose's `sealed class BottomHeader` (Title / Custom).
|
|
17
|
+
public struct BottomSheetHeader {
|
|
18
|
+
enum Style {
|
|
19
|
+
case title(String)
|
|
20
|
+
case custom(() -> AnyView)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let style: Style
|
|
24
|
+
|
|
25
|
+
/// Mirrors `BottomHeader.Title`.
|
|
26
|
+
public static func title(_ text: String = "Bottom Sheet Title") -> BottomSheetHeader {
|
|
27
|
+
BottomSheetHeader(style: .title(text))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Mirrors `BottomHeader.Custom`.
|
|
31
|
+
public static func custom<V: View>(@ViewBuilder _ content: @escaping () -> V) -> BottomSheetHeader {
|
|
32
|
+
BottomSheetHeader(style: .custom({ AnyView(content()) }))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// MARK: - Bottom sheet view
|
|
37
|
+
|
|
38
|
+
@available(iOS 16.0, *)
|
|
39
|
+
struct BottomSheetView<Content: View>: View {
|
|
40
|
+
let header: BottomSheetHeader
|
|
41
|
+
let isSurface: Bool
|
|
42
|
+
let barrierDismissible: Bool
|
|
43
|
+
let content: () -> Content
|
|
44
|
+
|
|
45
|
+
@EnvironmentObject private var navigator: Navigator
|
|
46
|
+
@Environment(\.appTheme) private var theme
|
|
47
|
+
@Environment(\.applicationEnvironment) private var appEnv
|
|
48
|
+
|
|
49
|
+
/// Vertical translation of the sheet; starts off-screen and animates to 0.
|
|
50
|
+
@State private var sheetOffset: CGFloat = UIScreen.main.bounds.height
|
|
51
|
+
/// Mirrors Compose's `backgroundAlpha`: only touched by open()/close(), never by drag.
|
|
52
|
+
@State private var backgroundAlpha: CGFloat = 0
|
|
53
|
+
@State private var sheetHeight: CGFloat = 0
|
|
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
|
|
61
|
+
|
|
62
|
+
/// Mirrors Compose's `sheetCloseOffset` (100dp drag threshold to dismiss).
|
|
63
|
+
private let closeThreshold: CGFloat = 100
|
|
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
|
+
|
|
69
|
+
private var screenHeight: CGFloat { UIScreen.main.bounds.height }
|
|
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 }
|
|
74
|
+
|
|
75
|
+
private var backgroundColor: Color {
|
|
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
|
|
89
|
+
}
|
|
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
|
+
|
|
96
|
+
var body: some View {
|
|
97
|
+
ZStack(alignment: .bottom) {
|
|
98
|
+
// Dimmed scrim — additive backgroundAlpha + dynamicAlpha (see above).
|
|
99
|
+
Color.black.opacity(totalScrimAlpha)
|
|
100
|
+
.contentShape(Rectangle())
|
|
101
|
+
.onTapGesture {
|
|
102
|
+
if barrierDismissible { close() }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
sheet
|
|
106
|
+
.offset(y: effectiveOffset)
|
|
107
|
+
}
|
|
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
|
+
}
|
|
116
|
+
.onAppear {
|
|
117
|
+
guard !didAppear else { return }
|
|
118
|
+
didAppear = true
|
|
119
|
+
ScreenTracker.trackPopupDisplayed(
|
|
120
|
+
maxApi: appEnv.maxApi,
|
|
121
|
+
context: appEnv.applicationContext,
|
|
122
|
+
parentScreenName: ScreenTracker.getLastScreenName()
|
|
123
|
+
)
|
|
124
|
+
navigator.registerOverplayDismiss { close() }
|
|
125
|
+
open()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private var sheet: some View {
|
|
130
|
+
VStack(spacing: 0) {
|
|
131
|
+
// Drag handle + header (the draggable region).
|
|
132
|
+
VStack(spacing: 0) {
|
|
133
|
+
Spacer().frame(height: 8)
|
|
134
|
+
Capsule()
|
|
135
|
+
.fill(Colors.black06)
|
|
136
|
+
.frame(width: 40, height: 4)
|
|
137
|
+
Spacer().frame(height: 4)
|
|
138
|
+
headerView
|
|
139
|
+
.frame(height: 56)
|
|
140
|
+
.padding(.horizontal, Spacing.M)
|
|
141
|
+
}
|
|
142
|
+
.frame(maxWidth: .infinity)
|
|
143
|
+
.frame(height: 72)
|
|
144
|
+
.contentShape(Rectangle())
|
|
145
|
+
.gesture(dragGesture)
|
|
146
|
+
|
|
147
|
+
Rectangle()
|
|
148
|
+
.fill(theme.colors.border.default)
|
|
149
|
+
.frame(height: 1)
|
|
150
|
+
|
|
151
|
+
content()
|
|
152
|
+
|
|
153
|
+
backgroundColor
|
|
154
|
+
.frame(height: effectiveBottomInset)
|
|
155
|
+
}
|
|
156
|
+
.frame(maxWidth: .infinity)
|
|
157
|
+
.frame(alignment: .bottom)
|
|
158
|
+
.background(
|
|
159
|
+
GeometryReader { geo in
|
|
160
|
+
Color.clear.onAppear { sheetHeight = geo.size.height }
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
.background(backgroundColor)
|
|
164
|
+
.clipShape(RoundedCorner(radius: Radius.M, corners: [.topLeft, .topRight]))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@ViewBuilder
|
|
168
|
+
private var headerView: some View {
|
|
169
|
+
switch header.style {
|
|
170
|
+
case .title(let text):
|
|
171
|
+
HStack(spacing: 0) {
|
|
172
|
+
Color.clear.frame(width: 24, height: 24)
|
|
173
|
+
MomoText(text, typography: .headerDefaultBold, color: Colors.black17)
|
|
174
|
+
.frame(maxWidth: .infinity, alignment: .center)
|
|
175
|
+
Icon(source: "navigation_close", size: 24, color: Colors.black17)
|
|
176
|
+
.frame(width: 24, height: 24)
|
|
177
|
+
.contentShape(Rectangle())
|
|
178
|
+
.onTapGesture { close() }
|
|
179
|
+
}
|
|
180
|
+
case .custom(let builder):
|
|
181
|
+
builder()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// MARK: - Drag handling (mirrors Compose's detectDragGestures)
|
|
186
|
+
|
|
187
|
+
private var dragGesture: some Gesture {
|
|
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)
|
|
207
|
+
}
|
|
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 {
|
|
215
|
+
close()
|
|
216
|
+
} else {
|
|
217
|
+
withAnimation(.spring(response: 0.3, dampingFraction: 0.9)) {
|
|
218
|
+
sheetOffset = 0
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// MARK: - Open / close animations
|
|
225
|
+
|
|
226
|
+
private func open() {
|
|
227
|
+
DispatchQueue.main.async {
|
|
228
|
+
withAnimation(.timingCurve(0.05, 0.7, 0.1, 1, duration: 0.1)) { backgroundAlpha = maxDynamicAlpha }
|
|
229
|
+
withAnimation(.timingCurve(0.05, 0.7, 0.1, 1, duration: 0.35)) { sheetOffset = 0 }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
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.
|
|
235
|
+
private func close() {
|
|
236
|
+
withAnimation(.timingCurve(0.3, 0, 0.8, 0.15, duration: 0.2)) {
|
|
237
|
+
sheetOffset = max(sheetHeight, screenHeight)
|
|
238
|
+
}
|
|
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) {
|
|
245
|
+
navigator.finishOverplayDismiss()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@available(iOS 16.0, *)
|
|
251
|
+
private func safeAreaBottomInset() -> CGFloat {
|
|
252
|
+
let bottomInset = UIApplication.shared.connectedScenes
|
|
253
|
+
.compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
|
|
254
|
+
.first ?? 0
|
|
255
|
+
return min(bottomInset, 21)
|
|
256
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ModalScreen.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `ModalScreen` (navigation/ModalScreen.kt).
|
|
6
|
+
// A centered overlay that fades + scales in over a dimmed scrim, and animates
|
|
7
|
+
// back out before the overplay is removed. Driven by `Navigator.showModal`.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import SwiftUI
|
|
11
|
+
|
|
12
|
+
@available(iOS 16.0, *)
|
|
13
|
+
struct ModalScreen<Content: View>: View {
|
|
14
|
+
let barrierDismissible: Bool
|
|
15
|
+
let content: () -> Content
|
|
16
|
+
|
|
17
|
+
@EnvironmentObject private var navigator: Navigator
|
|
18
|
+
@Environment(\.applicationEnvironment) private var appEnv
|
|
19
|
+
|
|
20
|
+
@State private var scrimOpacity: CGFloat = 0
|
|
21
|
+
@State private var contentScale: CGFloat = 0.8
|
|
22
|
+
@State private var contentOpacity: CGFloat = 0
|
|
23
|
+
@State private var didAppear = false
|
|
24
|
+
|
|
25
|
+
var body: some View {
|
|
26
|
+
ZStack {
|
|
27
|
+
// Dimmed scrim (mirrors `Color.Black.copy(alpha = 0.6f * alpha)`).
|
|
28
|
+
Color.black.opacity(0.6 * scrimOpacity)
|
|
29
|
+
.ignoresSafeArea()
|
|
30
|
+
.contentShape(Rectangle())
|
|
31
|
+
.onTapGesture {
|
|
32
|
+
if barrierDismissible { navigator.hideOverplay() }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
content()
|
|
36
|
+
.scaleEffect(contentScale)
|
|
37
|
+
.opacity(contentOpacity)
|
|
38
|
+
}
|
|
39
|
+
.onAppear {
|
|
40
|
+
guard !didAppear else { return }
|
|
41
|
+
didAppear = true
|
|
42
|
+
ScreenTracker.trackPopupDisplayed(
|
|
43
|
+
maxApi: appEnv.maxApi,
|
|
44
|
+
context: appEnv.applicationContext,
|
|
45
|
+
parentScreenName: ScreenTracker.getLastScreenName()
|
|
46
|
+
)
|
|
47
|
+
navigator.registerOverplayDismiss { close() }
|
|
48
|
+
open()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Mirrors Compose's `openEvent`: 250ms fade + scale to 1.
|
|
53
|
+
private func open() {
|
|
54
|
+
DispatchQueue.main.async {
|
|
55
|
+
withAnimation(.easeOut(duration: 0.25)) {
|
|
56
|
+
scrimOpacity = 1
|
|
57
|
+
contentScale = 1
|
|
58
|
+
contentOpacity = 1
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Mirrors Compose's `closeEvent`: 200ms fade + scale back to 0.8, then remove.
|
|
64
|
+
private func close() {
|
|
65
|
+
withAnimation(.easeIn(duration: 0.2)) {
|
|
66
|
+
scrimOpacity = 0
|
|
67
|
+
contentScale = 0.8
|
|
68
|
+
contentOpacity = 0
|
|
69
|
+
}
|
|
70
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
71
|
+
navigator.finishOverplayDismiss()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
//
|
|
2
|
+
// SnackBar.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `SnackBar` (navigation/component/SnackBar.kt).
|
|
6
|
+
// A snack bar slides up from the bottom (just above the footer / home
|
|
7
|
+
// indicator), stays for `duration`, then slides back down and is removed.
|
|
8
|
+
// `Navigator.showSnackBar(_:)` drives it through the overplay host.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
import SwiftUI
|
|
13
|
+
|
|
14
|
+
// MARK: - SnackBar model
|
|
15
|
+
|
|
16
|
+
/// Mirrors Compose's `sealed class SnackBar` (Custom / Toast). A `nil` duration
|
|
17
|
+
/// keeps the snack bar on screen until dismissed manually.
|
|
18
|
+
public struct SnackBar {
|
|
19
|
+
public enum Style {
|
|
20
|
+
case custom
|
|
21
|
+
case toast
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public let style: Style
|
|
25
|
+
public let duration: TimeInterval?
|
|
26
|
+
public let content: (() -> AnyView)?
|
|
27
|
+
|
|
28
|
+
private init(style: Style, duration: TimeInterval?, content: (() -> AnyView)?) {
|
|
29
|
+
self.style = style
|
|
30
|
+
self.duration = duration
|
|
31
|
+
self.content = content
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Mirrors `SnackBar.Custom`: renders arbitrary content.
|
|
35
|
+
public static func custom<V: View>(
|
|
36
|
+
duration: TimeInterval? = 3.0,
|
|
37
|
+
@ViewBuilder content: @escaping () -> V
|
|
38
|
+
) -> SnackBar {
|
|
39
|
+
SnackBar(style: .custom, duration: duration, content: { AnyView(content()) })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Mirrors `SnackBar.Toast`: a placeholder with no custom content (parity with
|
|
43
|
+
/// Compose where `Toast` currently renders nothing).
|
|
44
|
+
public static func toast(duration: TimeInterval? = 3.0) -> SnackBar {
|
|
45
|
+
SnackBar(style: .toast, duration: duration, content: nil)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// MARK: - SnackBar host view
|
|
50
|
+
|
|
51
|
+
/// Self-animating container. Slides the content up on appear, schedules an
|
|
52
|
+
/// auto-dismiss after `duration`, and animates out before removing the overplay.
|
|
53
|
+
@available(iOS 16.0, *)
|
|
54
|
+
struct SnackBarView: View {
|
|
55
|
+
let snackBar: SnackBar
|
|
56
|
+
/// Bottom inset so the bar sits above the footer / home indicator
|
|
57
|
+
/// (mirrors Compose's `footerHeight` offset).
|
|
58
|
+
let bottomInset: CGFloat
|
|
59
|
+
|
|
60
|
+
@EnvironmentObject private var navigator: Navigator
|
|
61
|
+
|
|
62
|
+
@State private var contentHeight: CGFloat = 0
|
|
63
|
+
@State private var offsetY: CGFloat = 0
|
|
64
|
+
@State private var didAppear = false
|
|
65
|
+
@State private var isClosing = false
|
|
66
|
+
@State private var autoDismissWorkItem: DispatchWorkItem?
|
|
67
|
+
|
|
68
|
+
var body: some View {
|
|
69
|
+
VStack(spacing: 0) {
|
|
70
|
+
Spacer(minLength: 0)
|
|
71
|
+
content
|
|
72
|
+
.background(
|
|
73
|
+
GeometryReader { geo in
|
|
74
|
+
Color.clear.onAppear { contentHeight = geo.size.height }
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
.offset(y: offsetY)
|
|
78
|
+
.padding(.bottom, bottomInset)
|
|
79
|
+
}
|
|
80
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
81
|
+
.onAppear {
|
|
82
|
+
guard !didAppear else { return }
|
|
83
|
+
didAppear = true
|
|
84
|
+
offsetY = max(contentHeight, 200) + bottomInset
|
|
85
|
+
open()
|
|
86
|
+
registerClose()
|
|
87
|
+
scheduleAutoDismiss()
|
|
88
|
+
}
|
|
89
|
+
.onDisappear {
|
|
90
|
+
autoDismissWorkItem?.cancel()
|
|
91
|
+
autoDismissWorkItem = nil
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@ViewBuilder
|
|
96
|
+
private var content: some View {
|
|
97
|
+
switch snackBar.style {
|
|
98
|
+
case .custom:
|
|
99
|
+
snackBar.content?()
|
|
100
|
+
case .toast:
|
|
101
|
+
EmptyView()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Mirrors Compose's `openEvent`: 350ms slide up.
|
|
106
|
+
private func open() {
|
|
107
|
+
DispatchQueue.main.async {
|
|
108
|
+
withAnimation(.easeOut(duration: 0.35)) {
|
|
109
|
+
offsetY = 0
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Animated slide down, then remove (mirrors Compose's `closeEvent`/`hideSnackBar`).
|
|
115
|
+
private func close() {
|
|
116
|
+
guard !isClosing else { return }
|
|
117
|
+
isClosing = true
|
|
118
|
+
autoDismissWorkItem?.cancel()
|
|
119
|
+
autoDismissWorkItem = nil
|
|
120
|
+
|
|
121
|
+
withAnimation(.easeIn(duration: 0.2)) {
|
|
122
|
+
offsetY = max(contentHeight, 200) + bottomInset
|
|
123
|
+
}
|
|
124
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
125
|
+
navigator.finishOverplayDismiss()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private func registerClose() {
|
|
130
|
+
navigator.registerOverplayDismiss { close() }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private func scheduleAutoDismiss() {
|
|
134
|
+
guard let duration = snackBar.duration else { return }
|
|
135
|
+
let workItem = DispatchWorkItem {
|
|
136
|
+
// Only auto-dismiss if this snack bar is still the active overplay.
|
|
137
|
+
if navigator.overplay?.type == .snackBar {
|
|
138
|
+
navigator.hideSnackBar()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
autoDismissWorkItem = workItem
|
|
142
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: workItem)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ScreenTracker.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `navigation/tracking/ScreenTracker.kt`. Pure
|
|
6
|
+
// logic, no platform dependency beyond the `MaxApi` bridge.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
public final class ScreenTrackingState {
|
|
12
|
+
public var time: Double = Date().timeIntervalSince1970 * 1000
|
|
13
|
+
public var releaseNavigated: Bool = false
|
|
14
|
+
public var releaseLoad: Bool = false
|
|
15
|
+
public var releaseInteraction: Bool = false
|
|
16
|
+
public var previousScreenName: String?
|
|
17
|
+
public var action: String = "push"
|
|
18
|
+
|
|
19
|
+
public init() {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public enum ScreenTracker {
|
|
23
|
+
private static var lastScreenName: String?
|
|
24
|
+
|
|
25
|
+
public static func trackScreenNavigated(
|
|
26
|
+
maxApi: MaxApi?,
|
|
27
|
+
context: MiniAppContext?,
|
|
28
|
+
screenName: String,
|
|
29
|
+
action: String,
|
|
30
|
+
state: ScreenTrackingState
|
|
31
|
+
) {
|
|
32
|
+
guard !state.releaseNavigated else { return }
|
|
33
|
+
|
|
34
|
+
let previousScreen = lastScreenName
|
|
35
|
+
lastScreenName = screenName
|
|
36
|
+
|
|
37
|
+
var params = buildBaseParams(context: context, screenName: screenName)
|
|
38
|
+
params["pre_screen_name"] = previousScreen
|
|
39
|
+
params["state"] = "navigated"
|
|
40
|
+
params["action"] = action
|
|
41
|
+
maxApi?.track(eventName: "auto_screen_navigated", params: params)
|
|
42
|
+
|
|
43
|
+
state.releaseNavigated = true
|
|
44
|
+
state.previousScreenName = previousScreen
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public static func trackScreenDisplayed(
|
|
48
|
+
maxApi: MaxApi?,
|
|
49
|
+
context: MiniAppContext?,
|
|
50
|
+
screenName: String,
|
|
51
|
+
duration: Double,
|
|
52
|
+
state: ScreenTrackingState
|
|
53
|
+
) {
|
|
54
|
+
guard !state.releaseLoad else { return }
|
|
55
|
+
|
|
56
|
+
var params = buildBaseParams(context: context, screenName: screenName)
|
|
57
|
+
params["state"] = "load"
|
|
58
|
+
params["duration"] = duration
|
|
59
|
+
maxApi?.track(eventName: "auto_screen_displayed", params: params)
|
|
60
|
+
|
|
61
|
+
state.releaseLoad = true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static func trackScreenInteracted(
|
|
65
|
+
maxApi: MaxApi?,
|
|
66
|
+
context: MiniAppContext?,
|
|
67
|
+
screenName: String,
|
|
68
|
+
duration: Double,
|
|
69
|
+
totalDuration: Double,
|
|
70
|
+
state: ScreenTrackingState
|
|
71
|
+
) {
|
|
72
|
+
guard !state.releaseInteraction else { return }
|
|
73
|
+
|
|
74
|
+
var params = buildBaseParams(context: context, screenName: screenName)
|
|
75
|
+
params["state"] = "interaction"
|
|
76
|
+
params["duration"] = duration
|
|
77
|
+
params["total_duration"] = totalDuration
|
|
78
|
+
maxApi?.track(eventName: "auto_screen_interacted", params: params)
|
|
79
|
+
|
|
80
|
+
state.releaseInteraction = true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public static func trackPopupDisplayed(
|
|
84
|
+
maxApi: MaxApi?,
|
|
85
|
+
context: MiniAppContext?,
|
|
86
|
+
parentScreenName: String?
|
|
87
|
+
) {
|
|
88
|
+
maxApi?.track(eventName: "auto_popup_displayed", params: [
|
|
89
|
+
"screen_name": parentScreenName,
|
|
90
|
+
"tracking_source": 1,
|
|
91
|
+
"app_id": context?.appId,
|
|
92
|
+
"feature_code": context?.appCode,
|
|
93
|
+
"kits_version": nil,
|
|
94
|
+
"component_name": "Modal"
|
|
95
|
+
])
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private static func buildBaseParams(
|
|
99
|
+
context: MiniAppContext?,
|
|
100
|
+
screenName: String,
|
|
101
|
+
componentName: String = "Screen"
|
|
102
|
+
) -> [String: Any?] {
|
|
103
|
+
[
|
|
104
|
+
"app_id": context?.appId,
|
|
105
|
+
"feature_code": context?.appCode,
|
|
106
|
+
"screen_name": screenName,
|
|
107
|
+
"component_name": componentName,
|
|
108
|
+
"tracking_source": 1
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public static func getLastScreenName() -> String? { lastScreenName }
|
|
113
|
+
|
|
114
|
+
public static func reset() {
|
|
115
|
+
lastScreenName = nil
|
|
116
|
+
}
|
|
117
|
+
}
|