@momo-kits/native-kits 0.162.2-beta.1 → 0.162.2-sp.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/.claude/settings.local.json +55 -0
- package/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +186 -0
- package/compose/build.gradle.kts.backup +186 -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/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 +57 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +109 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -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 +305 -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 +921 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +121 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +403 -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 +85 -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 +340 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +198 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +123 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +94 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +136 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +224 -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 +148 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +116 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +452 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +172 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +255 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +233 -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 +241 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -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 +92 -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 +352 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +103 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +70 -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 +17 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +96 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +348 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +256 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +494 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +215 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +96 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +531 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +92 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +135 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +214 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +590 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +177 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +192 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +205 -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 +239 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -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 +285 -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 +57 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -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 +14 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +50 -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 +133 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +145 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +345 -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 +556 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +161 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +243 -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 +187 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +315 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -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 +370 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +167 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +72 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +66 -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 +70 -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 +33 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +186 -0
- package/gradle/libs.versions.toml +67 -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 +34 -43
- package/ios/Application/FloatingButton.swift +10 -8
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +102 -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 +151 -0
- package/ios/Application/Navigation/NavigationOptions.swift +109 -0
- package/ios/Application/Navigation/Navigator.swift +360 -0
- package/ios/Application/Navigation/Overplay/BottomSheet.swift +176 -0
- package/ios/Application/Navigation/Overplay/ModalScreen.swift +68 -0
- package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -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/StackScreen.swift +449 -0
- package/ios/Avatar/Avatar.swift +193 -0
- package/ios/Badge/Badge.swift +11 -13
- package/ios/Badge/BadgeRibbon.swift +12 -19
- package/ios/BaselineView/BaselineView.swift +163 -0
- package/ios/Button/Button.swift +62 -23
- package/ios/Carousel/Carousel.swift +181 -0
- package/ios/Checkbox/Checkbox.swift +14 -12
- package/ios/Chip/Chip.swift +23 -9
- package/ios/Collapse/Collapse.swift +182 -0
- package/ios/Colors+Radius+Spacing/Colors.swift +1 -1
- package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
- package/ios/DateTimePicker/DateTimePicker.swift +210 -0
- package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
- package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
- package/ios/DateTimePicker/WheelPicker.swift +161 -0
- package/ios/Divider/Divider.swift +16 -0
- package/ios/Icon/Icon.swift +6 -1
- package/ios/IconButton/IconButton.swift +112 -0
- package/ios/Information/Information.swift +151 -0
- package/ios/Input/Input.swift +42 -25
- package/ios/Input/InputPhoneNumber.swift +21 -19
- package/ios/Input/InputSearch.swift +101 -45
- package/ios/Input/InputTextArea.swift +67 -40
- package/ios/InputDropDown/InputDropDown.swift +198 -0
- package/ios/InputMoney/InputMoney.swift +335 -0
- package/ios/InputOTP/InputOTP.swift +210 -0
- package/ios/Loader/Loader.swift +113 -0
- package/ios/MoMoUIKits.podspec +5 -0
- package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
- 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 +10 -12
- package/ios/Popup/PopupInput.swift +2 -6
- package/ios/Popup/PopupNotify.swift +219 -0
- package/ios/ProgressInfo/ProgressInfo.swift +294 -0
- package/ios/Radio/Radio.swift +70 -0
- package/ios/Rating/Rating.swift +82 -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 +451 -0
- package/ios/SuggestAction/SuggestAction.swift +97 -0
- package/ios/Swipeable/SwipeCell.swift +185 -0
- package/ios/Swipeable/SwipeCellModel.swift +21 -0
- package/ios/TabView/TabView.swift +530 -0
- package/ios/Tag/Tag.swift +98 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
- package/ios/Title/Title.swift +269 -0
- package/ios/Tooltip/Tooltip.swift +482 -0
- package/ios/Typography/Text.swift +86 -24
- package/ios/Typography/Typography.swift +31 -6
- package/ios/Uploader/Uploader.swift +183 -0
- package/ios/native-kits.podspec +58 -8
- package/ios-demo/MoMoUIKitsDemo.podspec +18 -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 +72 -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 +261 -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/NavigationDemo.swift +227 -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 +50 -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 +50 -0
- package/scripts/gen-ios-color.mjs +136 -0
- package/settings.gradle.kts +64 -0
- package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +0 -32
- package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +0 -16
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -60
- package/ios/Typography/MomoFontScale.swift +0 -36
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
|
|
47
|
+
/// Vertical translation of the sheet; starts off-screen and animates to 0.
|
|
48
|
+
@State private var sheetOffset: CGFloat = UIScreen.main.bounds.height
|
|
49
|
+
@State private var scrimAlpha: CGFloat = 0
|
|
50
|
+
@State private var sheetHeight: CGFloat = 0
|
|
51
|
+
@State private var didAppear = false
|
|
52
|
+
|
|
53
|
+
/// Mirrors Compose's `sheetCloseOffset` (100dp drag threshold to dismiss).
|
|
54
|
+
private let closeThreshold: CGFloat = 100
|
|
55
|
+
|
|
56
|
+
private var screenHeight: CGFloat { UIScreen.main.bounds.height }
|
|
57
|
+
|
|
58
|
+
private var backgroundColor: Color {
|
|
59
|
+
// Compose uses background.surface (white) for `isSurface`, otherwise
|
|
60
|
+
// background.default. Both resolve to white in the current iOS palette.
|
|
61
|
+
isSurface ? Colors.black01 : Colors.black01
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var body: some View {
|
|
65
|
+
ZStack(alignment: .bottom) {
|
|
66
|
+
// Dimmed scrim (max 0.3 alpha, mirrors Compose).
|
|
67
|
+
Color.black.opacity(scrimAlpha)
|
|
68
|
+
.ignoresSafeArea()
|
|
69
|
+
.contentShape(Rectangle())
|
|
70
|
+
.onTapGesture {
|
|
71
|
+
if barrierDismissible { close() }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
sheet
|
|
75
|
+
.offset(y: sheetOffset)
|
|
76
|
+
}
|
|
77
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
78
|
+
.onAppear {
|
|
79
|
+
guard !didAppear else { return }
|
|
80
|
+
didAppear = true
|
|
81
|
+
navigator.registerOverplayDismiss { close() }
|
|
82
|
+
open()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private var sheet: some View {
|
|
87
|
+
VStack(spacing: 0) {
|
|
88
|
+
// Drag handle + header (the draggable region).
|
|
89
|
+
VStack(spacing: 0) {
|
|
90
|
+
Spacer().frame(height: 8)
|
|
91
|
+
Capsule()
|
|
92
|
+
.fill(Colors.black06)
|
|
93
|
+
.frame(width: 40, height: 4)
|
|
94
|
+
Spacer().frame(height: 4)
|
|
95
|
+
headerView
|
|
96
|
+
.frame(height: 56)
|
|
97
|
+
.padding(.horizontal, Spacing.M)
|
|
98
|
+
}
|
|
99
|
+
.frame(maxWidth: .infinity)
|
|
100
|
+
.frame(height: 72)
|
|
101
|
+
.contentShape(Rectangle())
|
|
102
|
+
.gesture(dragGesture)
|
|
103
|
+
|
|
104
|
+
Rectangle()
|
|
105
|
+
.fill(Colors.black04)
|
|
106
|
+
.frame(height: 1)
|
|
107
|
+
|
|
108
|
+
content()
|
|
109
|
+
}
|
|
110
|
+
.frame(maxWidth: .infinity)
|
|
111
|
+
.frame(maxHeight: screenHeight - safeAreaTopInset() - 90, alignment: .bottom)
|
|
112
|
+
.background(
|
|
113
|
+
GeometryReader { geo in
|
|
114
|
+
Color.clear.onAppear { sheetHeight = geo.size.height }
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
.background(backgroundColor)
|
|
118
|
+
.clipShape(RoundedCorner(radius: Radius.M, corners: [.topLeft, .topRight]))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@ViewBuilder
|
|
122
|
+
private var headerView: some View {
|
|
123
|
+
switch header.style {
|
|
124
|
+
case .title(let text):
|
|
125
|
+
HStack(spacing: 0) {
|
|
126
|
+
Color.clear.frame(width: 24, height: 24)
|
|
127
|
+
MomoText(text, typography: .headerDefaultBold, color: Colors.black17)
|
|
128
|
+
.frame(maxWidth: .infinity, alignment: .center)
|
|
129
|
+
Icon(source: "navigation_close", size: 24, color: Colors.black17)
|
|
130
|
+
.frame(width: 24, height: 24)
|
|
131
|
+
.contentShape(Rectangle())
|
|
132
|
+
.onTapGesture { close() }
|
|
133
|
+
}
|
|
134
|
+
case .custom(let builder):
|
|
135
|
+
builder()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// MARK: - Drag handling (mirrors Compose's detectDragGestures)
|
|
140
|
+
|
|
141
|
+
private var dragGesture: some Gesture {
|
|
142
|
+
DragGesture()
|
|
143
|
+
.onChanged { value in
|
|
144
|
+
let newOffset = max(value.translation.height, 0)
|
|
145
|
+
sheetOffset = newOffset
|
|
146
|
+
}
|
|
147
|
+
.onEnded { _ in
|
|
148
|
+
if sheetOffset > closeThreshold {
|
|
149
|
+
close()
|
|
150
|
+
} else {
|
|
151
|
+
withAnimation(.spring(response: 0.3, dampingFraction: 0.9)) {
|
|
152
|
+
sheetOffset = 0
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// MARK: - Open / close animations
|
|
159
|
+
|
|
160
|
+
private func open() {
|
|
161
|
+
DispatchQueue.main.async {
|
|
162
|
+
withAnimation(.easeOut(duration: 0.1)) { scrimAlpha = 0.3 }
|
|
163
|
+
withAnimation(.timingCurve(0.05, 0.7, 0.1, 1, duration: 0.35)) { sheetOffset = 0 }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private func close() {
|
|
168
|
+
withAnimation(.timingCurve(0.3, 0, 0.8, 0.15, duration: 0.2)) {
|
|
169
|
+
sheetOffset = max(sheetHeight, screenHeight)
|
|
170
|
+
scrimAlpha = 0
|
|
171
|
+
}
|
|
172
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
173
|
+
navigator.finishOverplayDismiss()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
|
|
19
|
+
@State private var scrimOpacity: CGFloat = 0
|
|
20
|
+
@State private var contentScale: CGFloat = 0.8
|
|
21
|
+
@State private var contentOpacity: CGFloat = 0
|
|
22
|
+
@State private var didAppear = false
|
|
23
|
+
|
|
24
|
+
var body: some View {
|
|
25
|
+
ZStack {
|
|
26
|
+
// Dimmed scrim (mirrors `Color.Black.copy(alpha = 0.6f * alpha)`).
|
|
27
|
+
Color.black.opacity(0.6 * scrimOpacity)
|
|
28
|
+
.ignoresSafeArea()
|
|
29
|
+
.contentShape(Rectangle())
|
|
30
|
+
.onTapGesture {
|
|
31
|
+
if barrierDismissible { navigator.hideOverplay() }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
content()
|
|
35
|
+
.scaleEffect(contentScale)
|
|
36
|
+
.opacity(contentOpacity)
|
|
37
|
+
}
|
|
38
|
+
.onAppear {
|
|
39
|
+
guard !didAppear else { return }
|
|
40
|
+
didAppear = true
|
|
41
|
+
navigator.registerOverplayDismiss { close() }
|
|
42
|
+
open()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Mirrors Compose's `openEvent`: 250ms fade + scale to 1.
|
|
47
|
+
private func open() {
|
|
48
|
+
DispatchQueue.main.async {
|
|
49
|
+
withAnimation(.easeOut(duration: 0.25)) {
|
|
50
|
+
scrimOpacity = 1
|
|
51
|
+
contentScale = 1
|
|
52
|
+
contentOpacity = 1
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Mirrors Compose's `closeEvent`: 200ms fade + scale back to 0.8, then remove.
|
|
58
|
+
private func close() {
|
|
59
|
+
withAnimation(.easeIn(duration: 0.2)) {
|
|
60
|
+
scrimOpacity = 0
|
|
61
|
+
contentScale = 0.8
|
|
62
|
+
contentOpacity = 0
|
|
63
|
+
}
|
|
64
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
65
|
+
navigator.finishOverplayDismiss()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -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,178 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Header.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `Header` (navigation/component/Header.kt).
|
|
6
|
+
// Renders the 52pt header bar: a circular back button (colors resolved via
|
|
7
|
+
// `getHeaderColor`), the title area (`HeaderContent` → plain title, user title
|
|
8
|
+
// or inline search), the header-right toolkit, and a scroll-triggered bottom
|
|
9
|
+
// shadow. The header band background itself is painted by `HeaderBackground`.
|
|
10
|
+
//
|
|
11
|
+
// Named `NavigationHeader` to coexist with the legacy `Header` in
|
|
12
|
+
// `Components.swift` (kept as-is, used as a package elsewhere; to be marked
|
|
13
|
+
// deprecated later). Used by the new `StackScreen` navigation path.
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
import SwiftUI
|
|
17
|
+
|
|
18
|
+
public struct NavigationHeader: View {
|
|
19
|
+
var headerType: HeaderType = .default
|
|
20
|
+
var titlePosition: TitlePosition
|
|
21
|
+
var title: String
|
|
22
|
+
/// Optional rich title (mirrors Compose's `HeaderTitle`); `.user` renders an
|
|
23
|
+
/// avatar group, `.default`/nil renders plain `title`.
|
|
24
|
+
var headerTitle: HeaderTitle?
|
|
25
|
+
var headerRight: (() -> any View)? = { HeaderRight() }
|
|
26
|
+
var goBack: (() -> Void)?
|
|
27
|
+
var opacity: CGFloat = 1
|
|
28
|
+
var animatedHeader: AnimatedHeader?
|
|
29
|
+
var scrollState: CGFloat = 0
|
|
30
|
+
var inputSearchProps: InputSearchProps?
|
|
31
|
+
var tintColor: Color?
|
|
32
|
+
/// Title opacity; used by the animated search header to fade the title out on scroll.
|
|
33
|
+
var titleOpacity: CGFloat = 1
|
|
34
|
+
|
|
35
|
+
public init(
|
|
36
|
+
headerType: HeaderType = .default,
|
|
37
|
+
titlePosition: TitlePosition,
|
|
38
|
+
title: String,
|
|
39
|
+
headerTitle: HeaderTitle? = nil,
|
|
40
|
+
headerRight: (() -> any View)? = { HeaderRight() },
|
|
41
|
+
goBack: (() -> Void)? = nil,
|
|
42
|
+
opacity: CGFloat = 1,
|
|
43
|
+
animatedHeader: AnimatedHeader? = nil,
|
|
44
|
+
scrollState: CGFloat = 0,
|
|
45
|
+
inputSearchProps: InputSearchProps? = nil,
|
|
46
|
+
tintColor: Color? = nil,
|
|
47
|
+
titleOpacity: CGFloat = 1
|
|
48
|
+
) {
|
|
49
|
+
self.headerType = headerType
|
|
50
|
+
self.titlePosition = titlePosition
|
|
51
|
+
self.title = title
|
|
52
|
+
self.headerTitle = headerTitle
|
|
53
|
+
self.headerRight = headerRight
|
|
54
|
+
self.goBack = goBack
|
|
55
|
+
self.opacity = opacity
|
|
56
|
+
self.animatedHeader = animatedHeader
|
|
57
|
+
self.scrollState = scrollState
|
|
58
|
+
self.inputSearchProps = inputSearchProps
|
|
59
|
+
self.tintColor = tintColor
|
|
60
|
+
self.titleOpacity = titleOpacity
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private var isAnimated: Bool { animatedHeader != nil }
|
|
64
|
+
private var isSurface: Bool { animatedHeader?.isSurface ?? true }
|
|
65
|
+
|
|
66
|
+
private var headerColor: HeaderColor {
|
|
67
|
+
getHeaderColor(isAnimated: isAnimated, isSurface: isSurface, opacity: opacity, tintColor: tintColor)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Centered title needs a full-width overlay to stay screen-centered, not
|
|
71
|
+
/// boxed between the back button and the header-right (mirrors Compose).
|
|
72
|
+
private var centerTitle: Bool {
|
|
73
|
+
guard inputSearchProps == nil else { return false }
|
|
74
|
+
let isDefault = headerTitle == nil || headerTitle?.defaultTitle != nil
|
|
75
|
+
return titlePosition == .center && isDefault
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public var body: some View {
|
|
79
|
+
if headerType != .none {
|
|
80
|
+
ZStack {
|
|
81
|
+
HStack(spacing: 0) {
|
|
82
|
+
if let goBack {
|
|
83
|
+
BackButton(color: headerColor, onBack: goBack)
|
|
84
|
+
Spacer().frame(width: Spacing.S)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if centerTitle {
|
|
88
|
+
Spacer()
|
|
89
|
+
} else {
|
|
90
|
+
titleArea(centered: false)
|
|
91
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if let headerRight {
|
|
95
|
+
AnyView(headerRight())
|
|
96
|
+
.background(
|
|
97
|
+
GeometryReader { geo in
|
|
98
|
+
Color.clear.preference(key: HeaderRightWidthKey.self, value: geo.size.width)
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
.padding(.horizontal, Spacing.M)
|
|
104
|
+
|
|
105
|
+
if centerTitle {
|
|
106
|
+
titleArea(centered: true)
|
|
107
|
+
.padding(.horizontal, Spacing.M)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
.frame(height: HEADER_HEIGHT)
|
|
111
|
+
.overlay(alignment: .bottom) { VerticalShadow(opacity: opacity) }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Mirrors Compose's `HeaderContent`: plain title, user title, or — when a
|
|
116
|
+
/// header search is active — the inline search field.
|
|
117
|
+
@ViewBuilder
|
|
118
|
+
private func titleArea(centered: Bool) -> some View {
|
|
119
|
+
if let inputSearchProps {
|
|
120
|
+
InputSearch(props: inputSearchProps)
|
|
121
|
+
} else if case let .user(userData)? = headerTitle {
|
|
122
|
+
if #available(iOS 16.0, *) {
|
|
123
|
+
HeaderUserView(userData)
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
MomoText(
|
|
127
|
+
headerTitle?.defaultTitle ?? title,
|
|
128
|
+
typography: .actionSBold,
|
|
129
|
+
color: headerColor.tintIconColor,
|
|
130
|
+
textAlign: centered ? .center : .leading,
|
|
131
|
+
maxLines: 1
|
|
132
|
+
)
|
|
133
|
+
.opacity(titleOpacity)
|
|
134
|
+
.truncationMode(.tail)
|
|
135
|
+
.frame(maxWidth: centered ? .infinity : nil, alignment: centered ? .center : .leading)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// MARK: - Back button
|
|
141
|
+
|
|
142
|
+
/// Mirrors Compose's `BackButton`: a 28pt circular control with the back arrow.
|
|
143
|
+
private struct BackButton: View {
|
|
144
|
+
let color: HeaderColor
|
|
145
|
+
let onBack: () -> Void
|
|
146
|
+
|
|
147
|
+
var body: some View {
|
|
148
|
+
SwiftUI.Button(action: onBack) {
|
|
149
|
+
Circle()
|
|
150
|
+
.fill(color.backgroundButton)
|
|
151
|
+
.overlay(Circle().stroke(color.borderColor, lineWidth: 0.2))
|
|
152
|
+
.frame(width: 28, height: 28)
|
|
153
|
+
.overlay(Icon(source: "arrow-back", size: 20, color: color.tintIconColor))
|
|
154
|
+
}
|
|
155
|
+
.buttonStyle(.plain)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// MARK: - Vertical shadow
|
|
160
|
+
|
|
161
|
+
/// Mirrors Compose's `VerticalShadow`: a 6pt drop shadow under the header that
|
|
162
|
+
/// only appears once the header is fully opaque (scrolled).
|
|
163
|
+
private struct VerticalShadow: View {
|
|
164
|
+
let opacity: CGFloat
|
|
165
|
+
|
|
166
|
+
var body: some View {
|
|
167
|
+
if opacity >= 1 {
|
|
168
|
+
LinearGradient(
|
|
169
|
+
gradient: Gradient(colors: [Color.black.opacity(0.05), .clear]),
|
|
170
|
+
startPoint: .top,
|
|
171
|
+
endPoint: .bottom
|
|
172
|
+
)
|
|
173
|
+
.frame(height: 6)
|
|
174
|
+
.frame(maxWidth: .infinity)
|
|
175
|
+
.offset(y: 6)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HeaderBackground.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `HeaderBackground` (navigation/component/HeaderBackground.kt).
|
|
6
|
+
// Paints the header band behind the `Header`: a flat surface for `.default`,
|
|
7
|
+
// the taller pink gradient for `.extended`, fading out as the screen scrolls.
|
|
8
|
+
// Stays transparent when the header is transparent or the content is full-screen.
|
|
9
|
+
//
|
|
10
|
+
// Named `NavigationHeaderBackground` to coexist with the legacy
|
|
11
|
+
// `HeaderBackground` in `Components.swift` (kept as-is, used as a package
|
|
12
|
+
// elsewhere; to be marked deprecated later). Used by the new `StackScreen`.
|
|
13
|
+
//
|
|
14
|
+
|
|
15
|
+
import SwiftUI
|
|
16
|
+
|
|
17
|
+
public struct NavigationHeaderBackground: View {
|
|
18
|
+
var headerType: HeaderType = .default
|
|
19
|
+
var scrollState: CGFloat
|
|
20
|
+
var headerTransparent: Bool = false
|
|
21
|
+
var fullScreenContent: Bool = false
|
|
22
|
+
|
|
23
|
+
public init(
|
|
24
|
+
headerType: HeaderType = .default,
|
|
25
|
+
scrollState: CGFloat = 0,
|
|
26
|
+
headerTransparent: Bool = false,
|
|
27
|
+
fullScreenContent: Bool = false
|
|
28
|
+
) {
|
|
29
|
+
self.headerType = headerType
|
|
30
|
+
self.scrollState = scrollState
|
|
31
|
+
self.headerTransparent = headerTransparent
|
|
32
|
+
self.fullScreenContent = fullScreenContent
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private func pinkGradient(topColor: Color, bottomColor: Color) -> LinearGradient {
|
|
36
|
+
LinearGradient(
|
|
37
|
+
gradient: Gradient(colors: [
|
|
38
|
+
topColor,
|
|
39
|
+
bottomColor
|
|
40
|
+
]),
|
|
41
|
+
startPoint: .top,
|
|
42
|
+
endPoint: .bottom
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public var body: some View {
|
|
47
|
+
let statusBarHeight = safeAreaTopInset()
|
|
48
|
+
let opacity = max(0, 1 - scrollState / CGFloat(HEADER_HEIGHT))
|
|
49
|
+
let minHeight = fullScreenContent ? 0 : statusBarHeight + HEADER_HEIGHT
|
|
50
|
+
let maxHeight: CGFloat = 154
|
|
51
|
+
let backgroundColor = headerTransparent ? Color.clear : Colors.black01
|
|
52
|
+
let topColor = opacity == 0 ? backgroundColor : Color(hex: 0xFDCADE)
|
|
53
|
+
|
|
54
|
+
Group {
|
|
55
|
+
switch headerType {
|
|
56
|
+
case .default:
|
|
57
|
+
ZStack(alignment: .bottom) {
|
|
58
|
+
backgroundColor
|
|
59
|
+
.frame(height: minHeight)
|
|
60
|
+
if !headerTransparent {
|
|
61
|
+
pinkGradient(topColor: topColor, bottomColor: backgroundColor)
|
|
62
|
+
.frame(height: maxHeight)
|
|
63
|
+
.frame(height: minHeight, alignment: .top)
|
|
64
|
+
.clipped()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
case .extended:
|
|
69
|
+
ZStack {
|
|
70
|
+
if !headerTransparent {
|
|
71
|
+
backgroundColor
|
|
72
|
+
.frame(height: maxHeight)
|
|
73
|
+
pinkGradient(topColor: topColor, bottomColor: backgroundColor)
|
|
74
|
+
.frame(height: maxHeight)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case .none:
|
|
79
|
+
EmptyView()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|