@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,485 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
// MARK: - Types
|
|
4
|
+
|
|
5
|
+
/// Tooltip placement relative to the anchor element.
|
|
6
|
+
public enum TooltipPlacement {
|
|
7
|
+
case top
|
|
8
|
+
case bottom
|
|
9
|
+
case left
|
|
10
|
+
case right
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// Cross-axis alignment of the tooltip relative to the anchor.
|
|
14
|
+
public enum TooltipAlign {
|
|
15
|
+
case start
|
|
16
|
+
case center
|
|
17
|
+
case end
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Describes a single action button displayed in the tooltip.
|
|
21
|
+
public struct TooltipButton {
|
|
22
|
+
public let title: String?
|
|
23
|
+
public let icon: String?
|
|
24
|
+
public let onPress: (() -> Void)?
|
|
25
|
+
|
|
26
|
+
public init(
|
|
27
|
+
title: String? = nil,
|
|
28
|
+
icon: String? = nil,
|
|
29
|
+
onPress: (() -> Void)? = nil
|
|
30
|
+
) {
|
|
31
|
+
self.title = title
|
|
32
|
+
self.icon = icon
|
|
33
|
+
self.onPress = onPress
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// State holder for controlling tooltip visibility imperatively.
|
|
38
|
+
/// Mirrors the Compose `TooltipState` / `rememberTooltipState()` pattern.
|
|
39
|
+
public final class TooltipState: ObservableObject {
|
|
40
|
+
@Published public private(set) var isVisible: Bool = false
|
|
41
|
+
|
|
42
|
+
public init() {}
|
|
43
|
+
|
|
44
|
+
/// Shows the tooltip.
|
|
45
|
+
public func show() {
|
|
46
|
+
isVisible = true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Hides the tooltip.
|
|
50
|
+
public func hide() {
|
|
51
|
+
isVisible = false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Toggles the tooltip visibility.
|
|
55
|
+
public func toggle() {
|
|
56
|
+
isVisible.toggle()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// MARK: - Constants
|
|
61
|
+
|
|
62
|
+
private enum TooltipConst {
|
|
63
|
+
static let offset = Spacing.S // TOOLTIP_OFFSET
|
|
64
|
+
static let arrowSize: CGFloat = 6 // ARROW_SIZE
|
|
65
|
+
static let maxWidth: CGFloat = 300
|
|
66
|
+
static let cornerRadius = Radius.S
|
|
67
|
+
static let arrowCornerRadius: CGFloat = 2
|
|
68
|
+
static let arrowEdgeMargin = Spacing.M
|
|
69
|
+
static let arrowOverlap: CGFloat = 1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// MARK: - Arrow Shape
|
|
73
|
+
|
|
74
|
+
/// Triangular arrow pointing away from the anchor in the given placement direction.
|
|
75
|
+
struct TooltipArrowShape: Shape {
|
|
76
|
+
let placement: TooltipPlacement
|
|
77
|
+
|
|
78
|
+
func path(in rect: CGRect) -> Path {
|
|
79
|
+
let w = rect.width
|
|
80
|
+
let h = rect.height
|
|
81
|
+
var path = Path()
|
|
82
|
+
switch placement {
|
|
83
|
+
case .top:
|
|
84
|
+
path.move(to: CGPoint(x: 0, y: 0))
|
|
85
|
+
path.addLine(to: CGPoint(x: w, y: 0))
|
|
86
|
+
path.addLine(to: CGPoint(x: w / 2, y: h))
|
|
87
|
+
case .bottom:
|
|
88
|
+
path.move(to: CGPoint(x: 0, y: h))
|
|
89
|
+
path.addLine(to: CGPoint(x: w, y: h))
|
|
90
|
+
path.addLine(to: CGPoint(x: w / 2, y: 0))
|
|
91
|
+
case .left:
|
|
92
|
+
path.move(to: CGPoint(x: 0, y: 0))
|
|
93
|
+
path.addLine(to: CGPoint(x: 0, y: h))
|
|
94
|
+
path.addLine(to: CGPoint(x: w, y: h / 2))
|
|
95
|
+
case .right:
|
|
96
|
+
path.move(to: CGPoint(x: w, y: 0))
|
|
97
|
+
path.addLine(to: CGPoint(x: w, y: h))
|
|
98
|
+
path.addLine(to: CGPoint(x: 0, y: h / 2))
|
|
99
|
+
}
|
|
100
|
+
path.closeSubpath()
|
|
101
|
+
return path
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// MARK: - Anchor bounds preference
|
|
106
|
+
|
|
107
|
+
private struct TooltipAnchorBoundsKey: PreferenceKey {
|
|
108
|
+
static var defaultValue: Anchor<CGRect>? = nil
|
|
109
|
+
static func reduce(value: inout Anchor<CGRect>?, nextValue: () -> Anchor<CGRect>?) {
|
|
110
|
+
value = nextValue() ?? value
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// MARK: - Tooltip
|
|
115
|
+
|
|
116
|
+
/// A tooltip component that wraps `content` (the anchor) and shows a positioned
|
|
117
|
+
/// tooltip popup with title, description, close button, and action buttons.
|
|
118
|
+
///
|
|
119
|
+
/// Port of the Compose / React Native `Tooltip` component.
|
|
120
|
+
public struct Tooltip<Content: View>: View {
|
|
121
|
+
@ObservedObject private var state: TooltipState
|
|
122
|
+
private let title: String?
|
|
123
|
+
private let description: String
|
|
124
|
+
private let buttons: [TooltipButton]
|
|
125
|
+
private let placement: TooltipPlacement
|
|
126
|
+
private let align: TooltipAlign
|
|
127
|
+
private let onVisibleChange: ((Bool) -> Void)?
|
|
128
|
+
private let onPressClose: (() -> Void)?
|
|
129
|
+
private let content: Content
|
|
130
|
+
|
|
131
|
+
public init(
|
|
132
|
+
state: TooltipState,
|
|
133
|
+
title: String? = nil,
|
|
134
|
+
description: String,
|
|
135
|
+
buttons: [TooltipButton] = [],
|
|
136
|
+
placement: TooltipPlacement = .top,
|
|
137
|
+
align: TooltipAlign = .center,
|
|
138
|
+
onVisibleChange: ((Bool) -> Void)? = nil,
|
|
139
|
+
onPressClose: (() -> Void)? = nil,
|
|
140
|
+
@ViewBuilder content: () -> Content
|
|
141
|
+
) {
|
|
142
|
+
self.state = state
|
|
143
|
+
self.title = title
|
|
144
|
+
self.description = description
|
|
145
|
+
self.buttons = buttons
|
|
146
|
+
self.placement = placement
|
|
147
|
+
self.align = align
|
|
148
|
+
self.onVisibleChange = onVisibleChange
|
|
149
|
+
self.onPressClose = onPressClose
|
|
150
|
+
self.content = content()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public var body: some View {
|
|
154
|
+
content
|
|
155
|
+
.anchorPreference(key: TooltipAnchorBoundsKey.self, value: .bounds) { $0 }
|
|
156
|
+
.overlayPreferenceValue(TooltipAnchorBoundsKey.self) { anchor in
|
|
157
|
+
GeometryReader { proxy in
|
|
158
|
+
if state.isVisible, let anchor = anchor {
|
|
159
|
+
let anchorRect = proxy[anchor]
|
|
160
|
+
TooltipPopupContent(
|
|
161
|
+
title: title,
|
|
162
|
+
description: description,
|
|
163
|
+
buttons: buttons,
|
|
164
|
+
placement: placement,
|
|
165
|
+
align: align,
|
|
166
|
+
onPressClose: onPressClose
|
|
167
|
+
)
|
|
168
|
+
.fixedSize()
|
|
169
|
+
.modifier(
|
|
170
|
+
TooltipPositionModifier(
|
|
171
|
+
anchorRect: anchorRect,
|
|
172
|
+
containerSize: proxy.size,
|
|
173
|
+
placement: placement,
|
|
174
|
+
align: align
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
.onChange(of: state.isVisible) { newValue in
|
|
181
|
+
onVisibleChange?(newValue)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// MARK: - Positioning
|
|
187
|
+
|
|
188
|
+
/// Positions the tooltip (already sized via `.fixedSize()`) relative to the anchor,
|
|
189
|
+
/// mirroring `TooltipPositionProvider.calculatePosition`.
|
|
190
|
+
private struct TooltipPositionModifier: ViewModifier {
|
|
191
|
+
let anchorRect: CGRect
|
|
192
|
+
let containerSize: CGSize
|
|
193
|
+
let placement: TooltipPlacement
|
|
194
|
+
let align: TooltipAlign
|
|
195
|
+
|
|
196
|
+
@State private var popupSize: CGSize = .zero
|
|
197
|
+
|
|
198
|
+
private var offsetPx: CGFloat {
|
|
199
|
+
TooltipConst.offset + TooltipConst.arrowSize
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
func body(content: Content) -> some View {
|
|
203
|
+
content
|
|
204
|
+
.background(
|
|
205
|
+
GeometryReader { p in
|
|
206
|
+
Color.clear
|
|
207
|
+
.onAppear { popupSize = p.size }
|
|
208
|
+
.onChange(of: p.size) { popupSize = $0 }
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
.offset(x: originX, y: originY)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private var originX: CGFloat {
|
|
215
|
+
let popW = popupSize.width
|
|
216
|
+
switch placement {
|
|
217
|
+
case .top, .bottom:
|
|
218
|
+
return alignHorizontal(anchorRect.minX, anchorRect.width, popW)
|
|
219
|
+
case .left:
|
|
220
|
+
return anchorRect.minX - popW - offsetPx
|
|
221
|
+
case .right:
|
|
222
|
+
return anchorRect.maxX + offsetPx
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private var originY: CGFloat {
|
|
227
|
+
let popH = popupSize.height
|
|
228
|
+
switch placement {
|
|
229
|
+
case .top:
|
|
230
|
+
return anchorRect.minY - popH - offsetPx
|
|
231
|
+
case .bottom:
|
|
232
|
+
return anchorRect.maxY + offsetPx
|
|
233
|
+
case .left, .right:
|
|
234
|
+
return alignVertical(anchorRect.minY, anchorRect.height, popH)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private func alignHorizontal(_ anchorX: CGFloat, _ anchorW: CGFloat, _ popW: CGFloat) -> CGFloat {
|
|
239
|
+
switch align {
|
|
240
|
+
case .start: return anchorX
|
|
241
|
+
case .end: return anchorX + anchorW - popW
|
|
242
|
+
case .center: return anchorX + (anchorW - popW) / 2
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private func alignVertical(_ anchorY: CGFloat, _ anchorH: CGFloat, _ popH: CGFloat) -> CGFloat {
|
|
247
|
+
switch align {
|
|
248
|
+
case .start: return anchorY
|
|
249
|
+
case .end: return anchorY + anchorH - popH
|
|
250
|
+
case .center: return anchorY + (anchorH - popH) / 2
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// MARK: - Popup content
|
|
256
|
+
|
|
257
|
+
private struct TooltipPopupContent: View {
|
|
258
|
+
let title: String?
|
|
259
|
+
let description: String?
|
|
260
|
+
let buttons: [TooltipButton]
|
|
261
|
+
let placement: TooltipPlacement
|
|
262
|
+
let align: TooltipAlign
|
|
263
|
+
let onPressClose: (() -> Void)?
|
|
264
|
+
|
|
265
|
+
var body: some View {
|
|
266
|
+
ZStack {
|
|
267
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
268
|
+
HStack(alignment: .top, spacing: 0) {
|
|
269
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
270
|
+
if let title = title, !title.isEmpty {
|
|
271
|
+
MomoText(title, typography: .headerSSemibold, color: Colors.black01)
|
|
272
|
+
.lineLimit(1)
|
|
273
|
+
.truncationMode(.tail)
|
|
274
|
+
.padding(.bottom, Spacing.XS)
|
|
275
|
+
}
|
|
276
|
+
if let description = description, !description.isEmpty {
|
|
277
|
+
MomoText(description, typography: .descriptionDefaultRegular, color: Colors.black01)
|
|
278
|
+
.lineLimit(2)
|
|
279
|
+
.truncationMode(.tail)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
283
|
+
|
|
284
|
+
if let onPressClose = onPressClose {
|
|
285
|
+
Spacer().frame(width: Spacing.M)
|
|
286
|
+
SwiftUI.Button(action: onPressClose) {
|
|
287
|
+
Icon(source: "navigation_close", size: 20, color: Colors.black01)
|
|
288
|
+
.frame(width: 20, height: 20)
|
|
289
|
+
}
|
|
290
|
+
.buttonStyle(.plain)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if !buttons.isEmpty {
|
|
295
|
+
Spacer().frame(height: Spacing.M)
|
|
296
|
+
TooltipButtons(buttons: buttons)
|
|
297
|
+
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
.padding(Spacing.M)
|
|
301
|
+
.frame(maxWidth: TooltipConst.maxWidth, alignment: .leading)
|
|
302
|
+
.background(Colors.black17)
|
|
303
|
+
.clipShape(RoundedRectangle(cornerRadius: TooltipConst.cornerRadius))
|
|
304
|
+
}
|
|
305
|
+
.overlay(arrow, alignment: arrowAlignment)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// MARK: Arrow
|
|
309
|
+
|
|
310
|
+
@ViewBuilder
|
|
311
|
+
private var arrow: some View {
|
|
312
|
+
let arrowWidth = TooltipConst.arrowSize * 2
|
|
313
|
+
let arrowHeight = TooltipConst.arrowSize
|
|
314
|
+
let isVertical = placement == .top || placement == .bottom
|
|
315
|
+
TooltipArrowShape(placement: placement)
|
|
316
|
+
.fill(Colors.black17)
|
|
317
|
+
.frame(
|
|
318
|
+
width: isVertical ? arrowWidth : arrowHeight,
|
|
319
|
+
height: isVertical ? arrowHeight : arrowWidth
|
|
320
|
+
)
|
|
321
|
+
.offset(x: arrowOffsetX, y: arrowOffsetY)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private var arrowAlignment: Alignment {
|
|
325
|
+
switch placement {
|
|
326
|
+
case .top:
|
|
327
|
+
switch align {
|
|
328
|
+
case .start: return .bottomLeading
|
|
329
|
+
case .center: return .bottom
|
|
330
|
+
case .end: return .bottomTrailing
|
|
331
|
+
}
|
|
332
|
+
case .bottom:
|
|
333
|
+
switch align {
|
|
334
|
+
case .start: return .topLeading
|
|
335
|
+
case .center: return .top
|
|
336
|
+
case .end: return .topTrailing
|
|
337
|
+
}
|
|
338
|
+
case .left:
|
|
339
|
+
switch align {
|
|
340
|
+
case .start: return .topTrailing
|
|
341
|
+
case .center: return .trailing
|
|
342
|
+
case .end: return .bottomTrailing
|
|
343
|
+
}
|
|
344
|
+
case .right:
|
|
345
|
+
switch align {
|
|
346
|
+
case .start: return .topLeading
|
|
347
|
+
case .center: return .leading
|
|
348
|
+
case .end: return .bottomLeading
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private var arrowOffsetX: CGFloat {
|
|
354
|
+
let arrowHeight = TooltipConst.arrowSize
|
|
355
|
+
switch placement {
|
|
356
|
+
case .left: return arrowHeight - TooltipConst.arrowOverlap
|
|
357
|
+
case .right: return -arrowHeight + TooltipConst.arrowOverlap
|
|
358
|
+
default:
|
|
359
|
+
switch align {
|
|
360
|
+
case .start: return TooltipConst.arrowEdgeMargin
|
|
361
|
+
case .end: return -TooltipConst.arrowEdgeMargin
|
|
362
|
+
case .center: return 0
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private var arrowOffsetY: CGFloat {
|
|
368
|
+
let arrowHeight = TooltipConst.arrowSize
|
|
369
|
+
switch placement {
|
|
370
|
+
case .top: return arrowHeight - TooltipConst.arrowOverlap
|
|
371
|
+
case .bottom: return -arrowHeight + TooltipConst.arrowOverlap
|
|
372
|
+
default:
|
|
373
|
+
switch align {
|
|
374
|
+
case .start: return TooltipConst.arrowEdgeMargin
|
|
375
|
+
case .end: return -TooltipConst.arrowEdgeMargin
|
|
376
|
+
case .center: return 0
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// MARK: - Buttons row
|
|
383
|
+
|
|
384
|
+
private struct TooltipButtons: View {
|
|
385
|
+
let buttons: [TooltipButton]
|
|
386
|
+
|
|
387
|
+
var body: some View {
|
|
388
|
+
HStack(spacing: 0) {
|
|
389
|
+
content
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
@ViewBuilder
|
|
394
|
+
private var content: some View {
|
|
395
|
+
if buttons.count == 1 {
|
|
396
|
+
TooltipSingleButton(btn: buttons[0])
|
|
397
|
+
} else if buttons.count == 2 {
|
|
398
|
+
let primary = buttons[0]
|
|
399
|
+
let secondary = buttons[1]
|
|
400
|
+
let bothIcons = primary.icon != nil && secondary.icon != nil
|
|
401
|
+
if bothIcons {
|
|
402
|
+
TooltipIconButton(icon: secondary.icon ?? "", onPress: secondary.onPress ?? {})
|
|
403
|
+
Spacer().frame(width: Spacing.S)
|
|
404
|
+
TooltipIconButton(icon: primary.icon ?? "", onPress: primary.onPress ?? {})
|
|
405
|
+
} else {
|
|
406
|
+
TooltipSecondaryButton(title: secondary.title ?? "", onPress: secondary.onPress ?? {})
|
|
407
|
+
Spacer().frame(width: Spacing.S)
|
|
408
|
+
TooltipPrimaryButton(title: primary.title ?? "", onPress: primary.onPress ?? {})
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
ForEach(Array(buttons.enumerated()), id: \.offset) { index, btn in
|
|
412
|
+
if index > 0 {
|
|
413
|
+
Spacer().frame(width: Spacing.XXS)
|
|
414
|
+
}
|
|
415
|
+
TooltipSingleButton(btn: btn)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private func tooltipLimitWithEllipsis(_ value: String, max: Int = 16) -> String {
|
|
422
|
+
value.count > max ? String(value.prefix(max)) + "…" : value
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private struct TooltipSingleButton: View {
|
|
426
|
+
let btn: TooltipButton
|
|
427
|
+
|
|
428
|
+
var body: some View {
|
|
429
|
+
if let icon = btn.icon {
|
|
430
|
+
TooltipIconButton(icon: icon, onPress: btn.onPress ?? {})
|
|
431
|
+
} else {
|
|
432
|
+
TooltipPrimaryButton(title: btn.title ?? "", onPress: btn.onPress ?? {})
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private struct TooltipPrimaryButton: View {
|
|
438
|
+
let title: String
|
|
439
|
+
let onPress: () -> Void
|
|
440
|
+
|
|
441
|
+
var body: some View {
|
|
442
|
+
Button(
|
|
443
|
+
title: tooltipLimitWithEllipsis(title),
|
|
444
|
+
action: onPress,
|
|
445
|
+
type: .secondary,
|
|
446
|
+
size: .medium,
|
|
447
|
+
isFull: false
|
|
448
|
+
)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private struct TooltipSecondaryButton: View {
|
|
453
|
+
let title: String
|
|
454
|
+
let onPress: () -> Void
|
|
455
|
+
|
|
456
|
+
var body: some View {
|
|
457
|
+
SwiftUI.Button(action: onPress) {
|
|
458
|
+
MomoText(tooltipLimitWithEllipsis(title), typography: .actionSBold, color: Colors.black01)
|
|
459
|
+
.lineLimit(1)
|
|
460
|
+
.truncationMode(.tail)
|
|
461
|
+
.padding(.horizontal, Spacing.M)
|
|
462
|
+
.padding(.vertical, Spacing.S)
|
|
463
|
+
}
|
|
464
|
+
.buttonStyle(.plain)
|
|
465
|
+
// Mirrors Compose's `Modifier.weight(1f, fill = false)` on the secondary button:
|
|
466
|
+
// it yields space to the primary button first under width pressure.
|
|
467
|
+
.layoutPriority(-1)
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private struct TooltipIconButton: View {
|
|
472
|
+
let icon: String
|
|
473
|
+
let onPress: () -> Void
|
|
474
|
+
|
|
475
|
+
var body: some View {
|
|
476
|
+
let iconButtonSize = scaleSize(36)
|
|
477
|
+
SwiftUI.Button(action: onPress) {
|
|
478
|
+
Icon(source: icon, size: 24)
|
|
479
|
+
.frame(width: iconButtonSize, height: iconButtonSize)
|
|
480
|
+
.background(Colors.black01)
|
|
481
|
+
.clipShape(RoundedRectangle(cornerRadius: Radius.XL))
|
|
482
|
+
}
|
|
483
|
+
.buttonStyle(.plain)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import CoreGraphics
|
|
2
|
+
|
|
3
|
+
/// App-wide font-scale preference for the SwiftUI kit. An exported holder the host writes once at
|
|
4
|
+
/// app startup (from the cache-backed `FontScaleProvider`); `scaleSize`/`appFont` read the snapshot.
|
|
5
|
+
/// No realtime: the SwiftUI kit has no navigation container to scope reactive updates, so a startup
|
|
6
|
+
/// snapshot is enough. When `useOSScaleRate` is false the kit applies `userScaleRate` instead of the
|
|
7
|
+
/// OS Dynamic Type factor.
|
|
8
|
+
public final class FontScaleStore {
|
|
9
|
+
public static let shared = FontScaleStore()
|
|
10
|
+
private init() {}
|
|
11
|
+
|
|
12
|
+
public private(set) var userScaleRate: CGFloat = 1
|
|
13
|
+
public private(set) var useOSScaleRate: Bool = true
|
|
14
|
+
|
|
15
|
+
public func update(userScaleRate: CGFloat, useOSScaleRate: Bool) {
|
|
16
|
+
self.userScaleRate = userScaleRate
|
|
17
|
+
self.useOSScaleRate = useOSScaleRate
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
3
|
public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat? = nil) -> CGFloat {
|
|
4
|
+
if !FontScaleStore.shared.useOSScaleRate {
|
|
5
|
+
return size * FontScaleStore.shared.userScaleRate
|
|
6
|
+
}
|
|
4
7
|
let defaultScreenSize: CGFloat = 375
|
|
5
8
|
let maxFontScale: CGFloat = scaleRate ?? 1.5
|
|
6
9
|
let maxDeviceScale: CGFloat = 5
|
|
@@ -115,27 +118,98 @@ public extension TypographyStyle {
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
public struct MomoText: View {
|
|
121
|
+
@Environment(\.appTheme) private var theme
|
|
118
122
|
private let content: String
|
|
119
123
|
private let typography: TypographyStyle
|
|
120
|
-
private let color: Color
|
|
124
|
+
private let color: Color?
|
|
125
|
+
private let textAlign: TextAlignment
|
|
126
|
+
private let maxLines: Int
|
|
127
|
+
private let minLines: Int
|
|
128
|
+
private let lineLimitRange: ClosedRange<Int>?
|
|
129
|
+
private let truncationMode: Text.TruncationMode
|
|
130
|
+
private let strikethrough: Bool
|
|
131
|
+
private let underline: Bool
|
|
132
|
+
private let letterSpacing: CGFloat?
|
|
133
|
+
private let softWrap: Bool
|
|
134
|
+
private let accessibilityId: String?
|
|
121
135
|
|
|
122
136
|
public init(
|
|
123
137
|
_ content: String,
|
|
124
138
|
typography: TypographyStyle = .bodyDefaultRegular,
|
|
125
|
-
color: Color =
|
|
139
|
+
color: Color? = nil,
|
|
140
|
+
textAlign: TextAlignment = .leading,
|
|
141
|
+
maxLines: Int = Int.max,
|
|
142
|
+
minLines: Int = 1,
|
|
143
|
+
truncationMode: Text.TruncationMode = .tail,
|
|
144
|
+
strikethrough: Bool = false,
|
|
145
|
+
underline: Bool = false,
|
|
146
|
+
letterSpacing: CGFloat? = nil,
|
|
147
|
+
softWrap: Bool = true,
|
|
148
|
+
accessibilityId: String? = nil
|
|
126
149
|
) {
|
|
127
150
|
self.content = content
|
|
128
151
|
self.typography = typography
|
|
129
152
|
self.color = color
|
|
153
|
+
self.textAlign = textAlign
|
|
154
|
+
self.maxLines = maxLines
|
|
155
|
+
self.minLines = minLines
|
|
156
|
+
// SwiftUI's range-based `lineLimit` is iOS 16+ only; keep the range for
|
|
157
|
+
// newer OS versions and fall back to the legacy single-value overload.
|
|
158
|
+
if maxLines == Int.max && minLines <= 1 {
|
|
159
|
+
self.lineLimitRange = nil
|
|
160
|
+
} else {
|
|
161
|
+
let upper = maxLines == Int.max ? Int.max : Swift.max(maxLines, minLines)
|
|
162
|
+
self.lineLimitRange = Swift.max(1, minLines)...upper
|
|
163
|
+
}
|
|
164
|
+
self.truncationMode = truncationMode
|
|
165
|
+
self.strikethrough = strikethrough
|
|
166
|
+
self.underline = underline
|
|
167
|
+
self.letterSpacing = letterSpacing
|
|
168
|
+
self.softWrap = softWrap
|
|
169
|
+
self.accessibilityId = accessibilityId
|
|
130
170
|
}
|
|
131
171
|
|
|
132
172
|
public var body: some View {
|
|
133
|
-
let
|
|
173
|
+
let textColor = color ?? theme.colors.text.default
|
|
174
|
+
// Strikethrough is driven either by the typography variant (mirrors Compose
|
|
175
|
+
// *Strikethrough styles) or the explicit `strikethrough` flag.
|
|
176
|
+
let isStrikethrough = strikethrough || typography.isStrikethrough
|
|
177
|
+
|
|
178
|
+
var text = SwiftUI.Text(content)
|
|
134
179
|
.font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
|
|
135
|
-
.foregroundColor(
|
|
136
|
-
.
|
|
180
|
+
.foregroundColor(textColor)
|
|
181
|
+
.strikethrough(isStrikethrough, color: textColor)
|
|
182
|
+
.underline(underline, color: textColor)
|
|
183
|
+
|
|
184
|
+
if let letterSpacing = letterSpacing {
|
|
185
|
+
text = text.tracking(letterSpacing)
|
|
186
|
+
}
|
|
137
187
|
|
|
138
188
|
return text
|
|
189
|
+
.multilineTextAlignment(textAlign)
|
|
190
|
+
.lineSpacing(scaleSize(typography.lineHeight) - scaleSize(typography.fontSize))
|
|
191
|
+
.modifier(LineLimitModifier(range: lineLimitRange))
|
|
192
|
+
.truncationMode(truncationMode)
|
|
193
|
+
.fixedSize(horizontal: !softWrap, vertical: false)
|
|
194
|
+
.accessibilityIdentifier(accessibilityId ?? content)
|
|
139
195
|
.ignoreBoldTextSetting()
|
|
140
196
|
}
|
|
141
197
|
}
|
|
198
|
+
|
|
199
|
+
// Applies a line-limit range only on iOS 16+. Older targets use the legacy
|
|
200
|
+
// single-value overload so the file still compiles for iOS 15.
|
|
201
|
+
private struct LineLimitModifier: ViewModifier {
|
|
202
|
+
let range: ClosedRange<Int>?
|
|
203
|
+
|
|
204
|
+
func body(content: Content) -> some View {
|
|
205
|
+
if let range = range {
|
|
206
|
+
if #available(iOS 16.0, *) {
|
|
207
|
+
content.lineLimit(range)
|
|
208
|
+
} else {
|
|
209
|
+
content.lineLimit(range.upperBound)
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
content
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -11,6 +11,9 @@ public extension View {
|
|
|
11
11
|
|
|
12
12
|
public extension Font {
|
|
13
13
|
static func appFont(size: CGFloat) -> Font {
|
|
14
|
+
if !FontScaleStore.shared.useOSScaleRate {
|
|
15
|
+
return Font.system(size: size * FontScaleStore.shared.userScaleRate)
|
|
16
|
+
}
|
|
14
17
|
let defaultScreenSize: CGFloat = 375
|
|
15
18
|
let maxFontScale: CGFloat = 1.5
|
|
16
19
|
let maxDeviceScale: CGFloat = 5
|