@momo-kits/native-kits 0.0.1-beta.4 → 0.0.2-alpha.1
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 +99 -0
- package/PUBLISH_GUIDE.md +675 -0
- package/README.md +184 -5
- package/ios/Application/ApplicationEnvironment.swift +71 -0
- package/ios/Application/Components.swift +270 -0
- package/ios/Application/ComposeApi.swift +22 -0
- package/ios/Application/FloatingButton.swift +174 -0
- package/ios/Application/HeaderRight.swift +306 -0
- package/ios/Application/LanguageSource.swift +93 -0
- package/ios/Application/Localize.swift +378 -0
- package/ios/Application/LocalizeModifier.swift +30 -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 +213 -0
- package/ios/Application/Navigation/NavigationOptions.swift +109 -0
- package/ios/Application/Navigation/Navigator.swift +453 -0
- package/ios/Application/Navigation/NavigatorManager.swift +22 -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/Navigation/component/HeaderRightWidthKey.swift +11 -0
- package/ios/Application/Navigation/component/NavigationFooter.swift +90 -0
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +196 -0
- package/ios/Application/Screen.swift +250 -0
- package/ios/Application/StackScreen.swift +492 -0
- package/ios/Avatar/Avatar.swift +193 -0
- package/ios/Badge/Badge.swift +84 -0
- package/ios/Badge/BadgeDot.swift +31 -0
- package/ios/Badge/BadgeRibbon.swift +319 -0
- package/ios/BaselineView/BaselineView.swift +163 -0
- package/ios/Button/Button.swift +232 -84
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
- package/ios/Carousel/Carousel.swift +196 -0
- package/ios/Checkbox/Checkbox.swift +77 -0
- package/ios/Chip/Chip.swift +120 -0
- package/ios/Collapse/Collapse.swift +192 -0
- package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Colors.swift +42 -37
- package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
- package/ios/{Colors+Spacing → 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 +69 -0
- package/ios/IconButton/IconButton.swift +140 -0
- package/ios/Image/Image.swift +87 -0
- package/ios/Information/Information.swift +156 -0
- package/ios/Input/ErrorView.swift +58 -0
- package/ios/Input/Input.swift +461 -77
- package/ios/Input/InputPhoneNumber.swift +192 -0
- package/ios/Input/InputSearch.swift +301 -0
- package/ios/Input/InputTextArea.swift +318 -0
- package/ios/InputDropDown/InputDropDown.swift +201 -0
- package/ios/InputMoney/InputMoney.swift +405 -0
- package/ios/InputOTP/InputOTP.swift +211 -0
- package/ios/Layout/GridContext.swift +30 -0
- package/ios/Layout/Item.swift +42 -0
- package/ios/Layout/Section.swift +134 -0
- package/ios/Loader/Loader.swift +113 -0
- package/ios/Lottie/LottieView.swift +86 -0
- package/ios/MoMoUIKits.podspec +5 -0
- package/ios/OTPKeyboard/KeyboardButton.swift +13 -5
- package/ios/OTPKeyboard/OTPKeyboard.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 +279 -0
- package/ios/Popup/PopupInput.swift +92 -0
- package/ios/Popup/PopupNotify.swift +219 -0
- package/ios/Popup/PopupPromotion.swift +74 -0
- package/ios/PopupView/FullscreenPopup.swift +251 -0
- package/ios/{Popup → PopupView}/Modifiers.swift +6 -20
- package/ios/{Popup → PopupView}/PopupView.swift +38 -146
- package/ios/PopupView/Utils++.swift +281 -0
- package/ios/ProgressInfo/ProgressInfo.swift +297 -0
- package/ios/Radio/Radio.swift +69 -0
- package/ios/Rating/Rating.swift +83 -0
- package/{example/ios/Example/Preview Content/Preview Assets.xcassets/Contents.json → ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json} +5 -0
- package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Text.colorset/Contents.json +3 -3
- 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/ScrollIndicator/ScrollIndicator.swift +110 -0
- package/ios/Skeleton/Skeleton.swift +99 -0
- package/ios/Slider/Slider.swift +237 -0
- package/ios/StatusBarTap/StatusBarTap.h +13 -0
- package/ios/StatusBarTap/StatusBarTap.m +75 -0
- package/ios/Stepper/Stepper.swift +221 -0
- package/ios/Steps/Steps.swift +467 -0
- package/ios/SuggestAction/SuggestAction.swift +100 -0
- package/ios/Swipeable/SwipeCell.swift +226 -31
- package/ios/Swipeable/SwipeCellModel.swift +21 -0
- package/ios/Switch/Switch.swift +69 -0
- package/ios/TabView/TabView.swift +534 -0
- package/ios/Tag/Tag.swift +99 -0
- package/ios/Template/Logo/Logo.swift +75 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +123 -0
- package/ios/Title/Title.swift +270 -0
- package/ios/Tooltip/Tooltip.swift +486 -0
- package/ios/Typography/FontScale.swift +103 -0
- package/ios/Typography/FontScaleStore.swift +58 -0
- package/ios/Typography/KitDesignSystem.swift +17 -0
- package/ios/Typography/Text.swift +262 -0
- package/ios/Typography/Typography.swift +80 -23
- package/ios/Uploader/Uploader.swift +174 -0
- package/ios/native-kits.podspec +73 -13
- package/ios-demo/MoMoUIKitsDemo.podspec +20 -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 +107 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +75 -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 +108 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +96 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +60 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +76 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +102 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +100 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +420 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +56 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +276 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +93 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +81 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +65 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +96 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +117 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +94 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +94 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +88 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +120 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +85 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +87 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +73 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +235 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +241 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +68 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +95 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +67 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +101 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +64 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +68 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +83 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +61 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +90 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +82 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +93 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +114 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +90 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +107 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +58 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +114 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +86 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +181 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +104 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +118 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +85 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +53 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
- package/package.json +6 -6
- package/publish.sh +554 -20
- package/scripts/gen-ios-color.mjs +136 -0
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -114
- package/LICENSE +0 -20
- package/android/build.gradle +0 -82
- package/android/gradle.properties +0 -5
- package/android/src/main/AndroidManifest.xml +0 -4
- package/android/src/main/java/com/momoplatform/nativekits/NativeKitsPackage.kt +0 -17
- package/android/src/main/java/com/momoplatform/nativekits/NativeKitsViewManager.kt +0 -20
- package/example/ios/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -63
- package/example/ios/Example/ComponentDetail.swift +0 -35
- package/example/ios/Example/ComponentItem.swift +0 -24
- package/example/ios/Example/ComponentModel.swift +0 -33
- package/example/ios/Example/ContentView.swift +0 -53
- package/example/ios/Example/Example.entitlements +0 -10
- package/example/ios/Example/Foundation/ButtonDemo.swift +0 -80
- package/example/ios/Example/Foundation/Popup/ActionSheets.swift +0 -136
- package/example/ios/Example/Foundation/Popup/Floats.swift +0 -127
- package/example/ios/Example/Foundation/Popup/Popups.swift +0 -101
- package/example/ios/Example/Foundation/PopupDemo.swift +0 -366
- package/example/ios/Example/Foundation/TextInputDemo.swift +0 -31
- package/example/ios/Example/Foundation/TypographyDemo.swift +0 -53
- package/example/ios/Example/Foundation.swift +0 -26
- package/example/ios/Example/Info.plist +0 -18
- package/example/ios/Example/Library/SwipeableDemo.swift +0 -105
- package/example/ios/Example/Library.swift +0 -26
- package/example/ios/Example/Main.swift +0 -11
- package/example/ios/Example/Overview.swift +0 -20
- package/example/ios/Example/Resource/SFProText-Black.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Bold.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Heavy.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Light.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Medium.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Regular.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Semibold.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Thin.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Ultralight.ttf +0 -0
- package/example/ios/Example/Resource/foundation.json +0 -33
- package/example/ios/Example/Resource/library.json +0 -8
- package/example/ios/Example/Utils.swift +0 -30
- package/example/ios/Example/ViewModifier/DarkViewModifier.swift +0 -22
- package/example/ios/Example.xcodeproj/project.pbxproj +0 -570
- package/example/ios/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/example/ios/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/example/ios/Example.xcodeproj/project.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/example/ios/Example.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/ios/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/example/ios/Podfile +0 -6
- package/example/ios/Podfile.lock +0 -16
- package/example/ios/Pods/Local Podspecs/MoMoUIKits.podspec.json +0 -29
- package/example/ios/Pods/Manifest.lock +0 -16
- package/example/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -661
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -25
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-dummy.m +0 -5
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-prefix.pch +0 -12
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-umbrella.h +0 -16
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.debug.xcconfig +0 -12
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.modulemap +0 -6
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.release.xcconfig +0 -12
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown +0 -3
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist +0 -29
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m +0 -5
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h +0 -16
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig +0 -14
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap +0 -6
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig +0 -14
- package/ios/Colors+Spacing/Radius.swift +0 -11
- package/ios/Popup/Utils++.swift +0 -163
- package/ios/Theme.md +0 -18
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Background.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Border.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Card.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Error.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Primary.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/PrimaryDark.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/PrimaryLight.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Secondary.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Success.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/TextSecondary.colorset/Contents.json +0 -0
- /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Warning.colorset/Contents.json +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import Combine
|
|
3
|
+
|
|
4
|
+
public enum FABSize {
|
|
5
|
+
case small
|
|
6
|
+
case `default`
|
|
7
|
+
/// Backward-compatible alias for older iOS call sites. Compose names this
|
|
8
|
+
/// size `DEFAULT`.
|
|
9
|
+
case large
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public enum FABPosition {
|
|
13
|
+
case end
|
|
14
|
+
/// Backward-compatible alias for older iOS call sites. Compose names this
|
|
15
|
+
/// position `END`.
|
|
16
|
+
case right
|
|
17
|
+
case center
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public struct FabProps {
|
|
21
|
+
public let icon: String?
|
|
22
|
+
public let iconColor: Color?
|
|
23
|
+
public let label: String?
|
|
24
|
+
public let onClick: (() -> Void)?
|
|
25
|
+
public let containerColor: Color?
|
|
26
|
+
public let size: FABSize?
|
|
27
|
+
public let iconSize: CGFloat?
|
|
28
|
+
public let scrollOffset: CGFloat?
|
|
29
|
+
public let position: FABPosition?
|
|
30
|
+
public let bottomPadding: CGFloat?
|
|
31
|
+
public let renderComponent: (() -> AnyView)?
|
|
32
|
+
|
|
33
|
+
public init(
|
|
34
|
+
icon: String? = nil,
|
|
35
|
+
iconColor: Color? = .white,
|
|
36
|
+
label: String? = nil,
|
|
37
|
+
onClick: (() -> Void)? = nil,
|
|
38
|
+
containerColor: Color? = Colors.primary,
|
|
39
|
+
size: FABSize? = .default,
|
|
40
|
+
iconSize: CGFloat? = nil,
|
|
41
|
+
scrollOffset: CGFloat? = nil,
|
|
42
|
+
position: FABPosition? = .end,
|
|
43
|
+
bottomPadding: CGFloat? = nil,
|
|
44
|
+
renderComponent: (() -> AnyView)? = nil
|
|
45
|
+
) {
|
|
46
|
+
self.icon = icon
|
|
47
|
+
self.iconColor = iconColor
|
|
48
|
+
self.label = label
|
|
49
|
+
self.onClick = onClick
|
|
50
|
+
self.containerColor = containerColor
|
|
51
|
+
self.size = size
|
|
52
|
+
self.iconSize = iconSize
|
|
53
|
+
self.scrollOffset = scrollOffset
|
|
54
|
+
self.position = position
|
|
55
|
+
self.bottomPadding = bottomPadding
|
|
56
|
+
self.renderComponent = renderComponent
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public struct FloatingButton: View {
|
|
61
|
+
@State private var lastScrollOffset: CGFloat = 0
|
|
62
|
+
@State private var isExpanded: Bool = false
|
|
63
|
+
@State private var internalScrollOffset: CGFloat = 0
|
|
64
|
+
@State private var showText: Bool = true
|
|
65
|
+
@State private var fullWidth: CGFloat? = nil
|
|
66
|
+
@State private var currentWidth: CGFloat = 0
|
|
67
|
+
@State private var lastDirection: String? = nil
|
|
68
|
+
|
|
69
|
+
private let props: FabProps
|
|
70
|
+
private var keyboardOffset: CGFloat
|
|
71
|
+
private var scrollOffset: CGFloat
|
|
72
|
+
private var bottomPadding: CGFloat { props.bottomPadding ?? 12 + keyboardOffset}
|
|
73
|
+
private var position: FABPosition? { props.position ?? .end }
|
|
74
|
+
private var containerColor: Color? { props.containerColor ?? Colors.primary }
|
|
75
|
+
private var iconColor: Color? { props.iconColor ?? .white }
|
|
76
|
+
private var label: String? { props.label }
|
|
77
|
+
private var icon: String? { props.icon }
|
|
78
|
+
private var size: FABSize? { props.size }
|
|
79
|
+
private var iconSize: CGFloat? { props.iconSize ?? defaultIconSize}
|
|
80
|
+
private var onClick: (() -> Void)? { props.onClick }
|
|
81
|
+
private var renderComponent: (() -> AnyView)? { props.renderComponent }
|
|
82
|
+
|
|
83
|
+
public init(props: FabProps, keyboardOffset: CGFloat = 0, scrollOffset: CGFloat = 0) {
|
|
84
|
+
self.props = props
|
|
85
|
+
self.keyboardOffset = keyboardOffset
|
|
86
|
+
self.scrollOffset = scrollOffset
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private var height: CGFloat {
|
|
90
|
+
if(iconSize == nil) {
|
|
91
|
+
size == .small ? 36 : 48
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
iconSize!
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private var minWidth: CGFloat {
|
|
99
|
+
size == .small ? 36 : 48
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private var defaultIconSize: CGFloat {
|
|
103
|
+
size == .small ? 12 : 24
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public var body: some View {
|
|
107
|
+
Group {
|
|
108
|
+
if ((icon?.isEmpty) == nil), let render = renderComponent {
|
|
109
|
+
render()
|
|
110
|
+
} else {
|
|
111
|
+
content
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
.onTapGesture {
|
|
115
|
+
onClick?()
|
|
116
|
+
}
|
|
117
|
+
.padding(Spacing.M)
|
|
118
|
+
.frame(height: height + (Spacing.M * 2))
|
|
119
|
+
.background(containerColor)
|
|
120
|
+
.cornerRadius(100)
|
|
121
|
+
.padding(.bottom, bottomPadding)
|
|
122
|
+
.frame(alignment: position == .center ? .center : .trailing)
|
|
123
|
+
.onAppear {
|
|
124
|
+
internalScrollOffset = scrollOffset
|
|
125
|
+
isExpanded = label != nil
|
|
126
|
+
}
|
|
127
|
+
.onReceive(Just(scrollOffset)) { newValue in
|
|
128
|
+
let direction = newValue > internalScrollOffset ? "down" : "up"
|
|
129
|
+
|
|
130
|
+
if direction != lastDirection {
|
|
131
|
+
lastDirection = direction
|
|
132
|
+
if direction == "down" {
|
|
133
|
+
withAnimation(.easeOut(duration: 0.1)) {
|
|
134
|
+
isExpanded = false
|
|
135
|
+
}
|
|
136
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
137
|
+
showText = false
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
showText = true
|
|
141
|
+
withAnimation(.easeIn(duration: 0.1)) {
|
|
142
|
+
isExpanded = true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private var content: some View {
|
|
151
|
+
HStack(spacing: 0) {
|
|
152
|
+
ImageView(icon ?? "")
|
|
153
|
+
.scaledToFit()
|
|
154
|
+
.frame(width: iconSize, height: iconSize)
|
|
155
|
+
.foregroundColor(iconColor)
|
|
156
|
+
|
|
157
|
+
if let label = label {
|
|
158
|
+
if showText {
|
|
159
|
+
Spacer().frame(width: 8)
|
|
160
|
+
MomoText(label, typography: .actionDefaultBold, color: .white, maxLines: 1)
|
|
161
|
+
.background(
|
|
162
|
+
GeometryReader { geo in
|
|
163
|
+
Color.clear.onAppear {
|
|
164
|
+
fullWidth = geo.size.width
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
.opacity(isExpanded ? 1 : 0)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
public struct HeaderRightData {
|
|
5
|
+
var useShortcut: Bool
|
|
6
|
+
var useMore: Bool
|
|
7
|
+
var tools: [ToolGroup]
|
|
8
|
+
var toolCallback: ((String) -> Void)?
|
|
9
|
+
var useSystemTools: Bool
|
|
10
|
+
|
|
11
|
+
public init(
|
|
12
|
+
useShortcut: Bool = false,
|
|
13
|
+
useMore: Bool = false,
|
|
14
|
+
tools: [ToolGroup] = [],
|
|
15
|
+
toolCallback: ((String) -> Void)? = nil,
|
|
16
|
+
useSystemTools: Bool = true
|
|
17
|
+
) {
|
|
18
|
+
self.useShortcut = useShortcut
|
|
19
|
+
self.useMore = useMore
|
|
20
|
+
self.tools = tools
|
|
21
|
+
self.toolCallback = toolCallback
|
|
22
|
+
self.useSystemTools = useSystemTools
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public struct Tool {
|
|
27
|
+
var key: String
|
|
28
|
+
var icon: String
|
|
29
|
+
var showBadge: Bool
|
|
30
|
+
var name: [String: String]
|
|
31
|
+
var showRightIcon: Bool
|
|
32
|
+
|
|
33
|
+
public init(
|
|
34
|
+
key: String,
|
|
35
|
+
icon: String,
|
|
36
|
+
name: [String: String] = [:],
|
|
37
|
+
showBadge: Bool = false,
|
|
38
|
+
showRightIcon: Bool = true
|
|
39
|
+
) {
|
|
40
|
+
self.key = key
|
|
41
|
+
self.icon = icon
|
|
42
|
+
self.showBadge = showBadge
|
|
43
|
+
self.name = name
|
|
44
|
+
self.showRightIcon = showRightIcon
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func toMap() -> [String: Any] {
|
|
48
|
+
return [
|
|
49
|
+
"key": key,
|
|
50
|
+
"icon": icon,
|
|
51
|
+
"showBadge": showBadge,
|
|
52
|
+
"name": name,
|
|
53
|
+
"showRightIcon": showRightIcon
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public struct ToolGroup {
|
|
59
|
+
var title: [String: String]
|
|
60
|
+
var items: [Tool]
|
|
61
|
+
|
|
62
|
+
public init(
|
|
63
|
+
title: [String: String] = [:],
|
|
64
|
+
items: [Tool]
|
|
65
|
+
) {
|
|
66
|
+
self.title = title
|
|
67
|
+
self.items = items
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func toMap() -> [String: Any] {
|
|
71
|
+
return [
|
|
72
|
+
"title": title,
|
|
73
|
+
"items": items.map { $0.toMap() }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
struct NavigationButtonConfig {
|
|
79
|
+
let icon: String
|
|
80
|
+
let onPress: () -> Void
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// MARK: - Components
|
|
84
|
+
public struct HeaderRight: View {
|
|
85
|
+
public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
|
|
86
|
+
self.headerRight = headerRight
|
|
87
|
+
self.tintColor = tintColor
|
|
88
|
+
}
|
|
89
|
+
var headerRight: HeaderRightData?
|
|
90
|
+
var tintColor: Color? = nil
|
|
91
|
+
|
|
92
|
+
public var body: some View {
|
|
93
|
+
HStack(alignment: .center) {
|
|
94
|
+
ToolkitHeaderRight(headerRight: headerRight, tintColor: tintColor)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
struct ToolkitHeaderRight: View {
|
|
100
|
+
var headerRight: HeaderRightData?
|
|
101
|
+
var tintColor: Color?
|
|
102
|
+
@State private var isFavorite: Bool = false
|
|
103
|
+
@State private var isLoading: Bool = false
|
|
104
|
+
@EnvironmentObject private var environment: ApplicationEnvironment
|
|
105
|
+
|
|
106
|
+
// MARK: - Actions
|
|
107
|
+
private func onPressShortcut() {
|
|
108
|
+
// onToolAction needs the app context (appId/code) so the host knows which app to (un)favorite;
|
|
109
|
+
// without it the favorite is a no-op. Mirrors onPressMore below and the Compose HeaderRight.
|
|
110
|
+
let context = environment.applicationContext
|
|
111
|
+
environment.composeApi?.request(
|
|
112
|
+
funcName: "onToolAction",
|
|
113
|
+
params: [
|
|
114
|
+
"item": ["key": "onFavorite"],
|
|
115
|
+
"context": [
|
|
116
|
+
"appId": context?.appId,
|
|
117
|
+
"code": context?.appCode,
|
|
118
|
+
"name": context?.appName,
|
|
119
|
+
"icon": context?.appIcon,
|
|
120
|
+
"description": context?.description,
|
|
121
|
+
"support": context?.support,
|
|
122
|
+
"toolkitConfig": context?.toolkitConfig,
|
|
123
|
+
"providerId": context?.providerId,
|
|
124
|
+
"permissions": context?.permissions
|
|
125
|
+
]
|
|
126
|
+
]
|
|
127
|
+
) { _ in
|
|
128
|
+
isFavorite.toggle()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private func onPressHelpCenter() {
|
|
133
|
+
let context = environment.applicationContext
|
|
134
|
+
let paramMap: [String: Any] = [
|
|
135
|
+
"appId": context?.appId,
|
|
136
|
+
"code": context?.appCode,
|
|
137
|
+
"name": context?.appName,
|
|
138
|
+
"icon": context?.appIcon,
|
|
139
|
+
"description": context?.description
|
|
140
|
+
]
|
|
141
|
+
environment.composeApi?.request(
|
|
142
|
+
funcName: "showHelpCenter",
|
|
143
|
+
params: paramMap
|
|
144
|
+
) { _ in }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private func onPressClose() {
|
|
148
|
+
environment.composeApi?.request(
|
|
149
|
+
funcName: "dismissAll",
|
|
150
|
+
params: ""
|
|
151
|
+
) { _ in }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private func onPressMore() {
|
|
155
|
+
let context = environment.applicationContext
|
|
156
|
+
let params: [String: Any] = [
|
|
157
|
+
"useSystemTools": headerRight?.useSystemTools,
|
|
158
|
+
"tools": headerRight?.tools.map { $0.toMap() } ?? [],
|
|
159
|
+
"context": [
|
|
160
|
+
"appId": context?.appId,
|
|
161
|
+
"code": context?.appCode,
|
|
162
|
+
"name": context?.appName,
|
|
163
|
+
"icon": context?.appIcon,
|
|
164
|
+
"description": context?.description,
|
|
165
|
+
"support": context?.support,
|
|
166
|
+
"toolkitConfig": context?.toolkitConfig,
|
|
167
|
+
"providerId": context?.providerId,
|
|
168
|
+
"permissions": context?.permissions
|
|
169
|
+
]
|
|
170
|
+
]
|
|
171
|
+
environment.composeApi?.request(
|
|
172
|
+
funcName: "showTools",
|
|
173
|
+
params: params
|
|
174
|
+
) { response in
|
|
175
|
+
do {
|
|
176
|
+
if let jsonData = response.data(using: .utf8),
|
|
177
|
+
let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
|
|
178
|
+
let toolResponse = json["response"] as? String {
|
|
179
|
+
headerRight?.toolCallback?(toolResponse)
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
print("Error parsing response:", error)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Load the current favorite state on appear so the star reflects reality; without this the button
|
|
188
|
+
// always renders "not favorited" until the user taps it. Mirrors the Compose LaunchedEffect.
|
|
189
|
+
private func loadFavoriteState() {
|
|
190
|
+
let context = environment.applicationContext
|
|
191
|
+
environment.composeApi?.request(
|
|
192
|
+
funcName: "isFavoriteApp",
|
|
193
|
+
params: ["code": context?.appCode]
|
|
194
|
+
) { response in
|
|
195
|
+
guard let data = response.data(using: .utf8),
|
|
196
|
+
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
isFavorite = (json["response"] as? Bool) ?? false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private func getNavigationButtonConfig() -> NavigationButtonConfig {
|
|
204
|
+
let totalTools = headerRight?.tools.reduce(0) { $0 + $1.items.count } ?? 0
|
|
205
|
+
var icon = isFavorite ? "pin_star_checked" : "pin_star"
|
|
206
|
+
var onClickHandler: () -> Void = onPressShortcut
|
|
207
|
+
|
|
208
|
+
if totalTools > 1 || headerRight?.useMore == true {
|
|
209
|
+
icon = "navigation_more_icon"
|
|
210
|
+
onClickHandler = onPressMore
|
|
211
|
+
} else if totalTools == 1, let singleTool = headerRight?.tools.first?.items.first {
|
|
212
|
+
icon = singleTool.icon
|
|
213
|
+
onClickHandler = {
|
|
214
|
+
headerRight?.toolCallback?(singleTool.key)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return NavigationButtonConfig(icon: icon, onPress: onClickHandler)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
var body: some View {
|
|
222
|
+
let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
|
|
223
|
+
let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
|
|
224
|
+
let navButtonConfig = getNavigationButtonConfig()
|
|
225
|
+
let showBadge = headerRight?.tools.contains { group in
|
|
226
|
+
group.items.contains { $0.showBadge }
|
|
227
|
+
} ?? false
|
|
228
|
+
|
|
229
|
+
HStack(alignment: .center, spacing: 0) {
|
|
230
|
+
if headerRight?.useShortcut == true {
|
|
231
|
+
NavigationButton(
|
|
232
|
+
disabled: isLoading,
|
|
233
|
+
icon: navButtonConfig.icon,
|
|
234
|
+
showBadge: showBadge,
|
|
235
|
+
onClick: navButtonConfig.onPress,
|
|
236
|
+
tintColor: tintColor
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
HStack(alignment: .center, spacing: 0) {
|
|
241
|
+
Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
|
|
242
|
+
.padding(4)
|
|
243
|
+
.onTapGesture {
|
|
244
|
+
onPressHelpCenter()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Rectangle()
|
|
248
|
+
.fill(tintColor ?? Colors.black20)
|
|
249
|
+
.frame(width: 0.5, height: 12)
|
|
250
|
+
|
|
251
|
+
Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
|
|
252
|
+
.padding(4)
|
|
253
|
+
.onTapGesture {
|
|
254
|
+
onPressClose()
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
.frame(width: 65, height: 28)
|
|
258
|
+
.background(backgroundButtonColor)
|
|
259
|
+
.cornerRadius(14)
|
|
260
|
+
.overlay(
|
|
261
|
+
RoundedRectangle(cornerRadius: 14)
|
|
262
|
+
.stroke(borderColor, lineWidth: 0.2)
|
|
263
|
+
)
|
|
264
|
+
.padding(.leading, Spacing.S)
|
|
265
|
+
}
|
|
266
|
+
.onAppear {
|
|
267
|
+
loadFavoriteState()
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
struct NavigationButton: View {
|
|
273
|
+
var disabled: Bool
|
|
274
|
+
var icon: String
|
|
275
|
+
var showBadge: Bool
|
|
276
|
+
var onClick: () -> Void
|
|
277
|
+
var tintColor: Color?
|
|
278
|
+
|
|
279
|
+
var body: some View {
|
|
280
|
+
let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
|
|
281
|
+
let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
|
|
282
|
+
|
|
283
|
+
ZStack {
|
|
284
|
+
Circle()
|
|
285
|
+
.fill(backgroundButtonColor)
|
|
286
|
+
.overlay(
|
|
287
|
+
Circle()
|
|
288
|
+
.stroke(borderColor, lineWidth: 0.2)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
Icon(source: icon, size: 16, color: tintColor)
|
|
292
|
+
|
|
293
|
+
if showBadge {
|
|
294
|
+
BadgeDot(size: .small)
|
|
295
|
+
.offset(x: -2, y: -2)
|
|
296
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
.frame(width: 28, height: 28)
|
|
300
|
+
.onTapGesture {
|
|
301
|
+
if !disabled {
|
|
302
|
+
onClick()
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// File: ios/Application/LanguageSource.swift
|
|
2
|
+
// Created At: 2026-07-29 10:00:00 +07:00
|
|
3
|
+
// Created By: AI
|
|
4
|
+
// AI Agent: Claude Code
|
|
5
|
+
// Model: claude-opus-5[1m]
|
|
6
|
+
|
|
7
|
+
// AI-GENERATED START: observe the host app's active language so Localize can follow it
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
/// Read-only view of the host app's active language, plus change delivery.
|
|
11
|
+
/// Values handed to `onChange` are already normalized to `Localize.VI` / `Localize.EN`.
|
|
12
|
+
public protocol LanguageSource: AnyObject {
|
|
13
|
+
var currentLanguage: String { get }
|
|
14
|
+
/// `onChange` may be invoked on any thread; `Localize` dedupes and hops to main.
|
|
15
|
+
func startObserving(_ onChange: @escaping (String) -> Void)
|
|
16
|
+
func stopObserving()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Reads the language the MoMo host app persists in `UserDefaults`. All three host writers
|
|
20
|
+
/// (the `RNResource` RN bridge, `JSONLocalization`, and the KMP `LocalizationProvider`) write the
|
|
21
|
+
/// same key on `UserDefaults.standard` in-process, so KVO on it sees every one of them.
|
|
22
|
+
public final class UserDefaultsLanguageSource: NSObject, LanguageSource {
|
|
23
|
+
public static let defaultKey = "language"
|
|
24
|
+
|
|
25
|
+
private let defaults: UserDefaults
|
|
26
|
+
private let key: String
|
|
27
|
+
private var onChange: ((String) -> Void)?
|
|
28
|
+
private var isObserving = false
|
|
29
|
+
private var kvoContext = 0
|
|
30
|
+
|
|
31
|
+
/// - Note: `key` must not contain `.` — `forKeyPath:` would read it as a key path.
|
|
32
|
+
public init(
|
|
33
|
+
defaults: UserDefaults = .standard,
|
|
34
|
+
key: String = UserDefaultsLanguageSource.defaultKey
|
|
35
|
+
) {
|
|
36
|
+
self.defaults = defaults
|
|
37
|
+
self.key = key
|
|
38
|
+
super.init()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public var currentLanguage: String {
|
|
42
|
+
Localize.normalize(defaults.string(forKey: key))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public func startObserving(_ onChange: @escaping (String) -> Void) {
|
|
46
|
+
guard !isObserving else { return }
|
|
47
|
+
isObserving = true
|
|
48
|
+
self.onChange = onChange
|
|
49
|
+
defaults.addObserver(self, forKeyPath: key, options: [.new], context: &kvoContext)
|
|
50
|
+
// Safety net for writes KVO can miss (e.g. `removeObject`). Double delivery is free:
|
|
51
|
+
// `Localize` dedupes before touching `@Published`.
|
|
52
|
+
NotificationCenter.default.addObserver(
|
|
53
|
+
self,
|
|
54
|
+
selector: #selector(emit),
|
|
55
|
+
name: UserDefaults.didChangeNotification,
|
|
56
|
+
object: defaults
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public func stopObserving() {
|
|
61
|
+
guard isObserving else { return }
|
|
62
|
+
isObserving = false
|
|
63
|
+
defaults.removeObserver(self, forKeyPath: key, context: &kvoContext)
|
|
64
|
+
NotificationCenter.default.removeObserver(
|
|
65
|
+
self,
|
|
66
|
+
name: UserDefaults.didChangeNotification,
|
|
67
|
+
object: defaults
|
|
68
|
+
)
|
|
69
|
+
onChange = nil
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
deinit {
|
|
73
|
+
stopObserving()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public override func observeValue(
|
|
77
|
+
forKeyPath keyPath: String?,
|
|
78
|
+
of object: Any?,
|
|
79
|
+
change: [NSKeyValueChangeKey: Any]?,
|
|
80
|
+
context: UnsafeMutableRawPointer?
|
|
81
|
+
) {
|
|
82
|
+
guard context == &kvoContext else {
|
|
83
|
+
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
emit()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@objc private func emit() {
|
|
90
|
+
onChange?(currentLanguage)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// AI-GENERATED END: observe the host app's active language so Localize can follow it
|