@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,134 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/// 12-column section container: horizontal margin, optional background image, and a
|
|
4
|
+
/// wrapping row of children that read their span size from `\.gridContext`.
|
|
5
|
+
/// Mirrors `vn.momo.kits.layout.Section`.
|
|
6
|
+
public struct SectionView<Content: View>: View {
|
|
7
|
+
// MARK: Lifecycle
|
|
8
|
+
|
|
9
|
+
public init(
|
|
10
|
+
useMargin: Bool = true,
|
|
11
|
+
backgroundImage: String? = nil,
|
|
12
|
+
@ViewBuilder content: @escaping () -> Content
|
|
13
|
+
) {
|
|
14
|
+
self.useMargin = useMargin
|
|
15
|
+
self.backgroundImage = backgroundImage
|
|
16
|
+
self.content = content
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// MARK: Public
|
|
20
|
+
|
|
21
|
+
public var body: some View {
|
|
22
|
+
flow
|
|
23
|
+
.environment(\.gridContext, grid)
|
|
24
|
+
.frame(maxWidth: .infinity, alignment: .topLeading)
|
|
25
|
+
.background(widthReader)
|
|
26
|
+
.background(background)
|
|
27
|
+
.padding(.horizontal, useMargin ? Spacing.M : 0)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MARK: Private
|
|
31
|
+
|
|
32
|
+
private static var columns: Int { 12 }
|
|
33
|
+
private static var gutter: CGFloat { Spacing.M }
|
|
34
|
+
|
|
35
|
+
private let useMargin: Bool
|
|
36
|
+
private let backgroundImage: String?
|
|
37
|
+
private let content: () -> Content
|
|
38
|
+
|
|
39
|
+
/// Width available *inside* the horizontal margin — the Compose `BoxWithConstraints` equivalent.
|
|
40
|
+
@State private var availableWidth: CGFloat = 0
|
|
41
|
+
|
|
42
|
+
private var grid: GridContext {
|
|
43
|
+
let columns = CGFloat(Self.columns)
|
|
44
|
+
// The trailing `- 1 / columns` mirrors Compose: it absorbs rounding so 12 spans
|
|
45
|
+
// plus their gutters never overflow the row by a sub-point.
|
|
46
|
+
let sizePerSpan = (availableWidth - Self.gutter * (columns - 1)) / columns - 1 / columns
|
|
47
|
+
return GridContext(numberOfColumns: Self.columns, gutter: Self.gutter, sizePerSpan: max(0, sizePerSpan))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@ViewBuilder private var flow: some View {
|
|
51
|
+
if #available(iOS 16.0, *) {
|
|
52
|
+
FlowRowLayout(gutter: Self.gutter) { content() }
|
|
53
|
+
} else {
|
|
54
|
+
// No `Layout` protocol before iOS 16: children stack instead of wrapping.
|
|
55
|
+
// Full-width (span 12) items — the common case — look identical.
|
|
56
|
+
VStack(alignment: .leading, spacing: Self.gutter) { content() }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@ViewBuilder private var background: some View {
|
|
61
|
+
if let backgroundImage {
|
|
62
|
+
ImageView(backgroundImage, contentMode: .fill).clipped()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private var widthReader: some View {
|
|
67
|
+
GeometryReader { proxy in
|
|
68
|
+
Color.clear.preference(key: SectionWidthKey.self, value: proxy.size.width)
|
|
69
|
+
}
|
|
70
|
+
.onPreferenceChange(SectionWidthKey.self) { availableWidth = $0 }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private struct SectionWidthKey: PreferenceKey {
|
|
75
|
+
static var defaultValue: CGFloat = 0
|
|
76
|
+
|
|
77
|
+
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
78
|
+
value = max(value, nextValue())
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Wrapping row with equal horizontal/vertical spacing — the Compose `FlowRow` equivalent.
|
|
83
|
+
@available(iOS 16.0, *)
|
|
84
|
+
private struct FlowRowLayout: Layout {
|
|
85
|
+
struct Row {
|
|
86
|
+
var items: [(index: Int, size: CGSize)] = []
|
|
87
|
+
var width: CGFloat = 0
|
|
88
|
+
var height: CGFloat = 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let gutter: CGFloat
|
|
92
|
+
|
|
93
|
+
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) -> CGSize {
|
|
94
|
+
let maxWidth = proposal.width ?? .infinity
|
|
95
|
+
let rows = rows(for: subviews, maxWidth: maxWidth)
|
|
96
|
+
let height = rows.reduce(0) { $0 + $1.height } + gutter * CGFloat(max(0, rows.count - 1))
|
|
97
|
+
return CGSize(width: proposal.width ?? rows.map(\.width).max() ?? 0, height: height)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) {
|
|
101
|
+
var y = bounds.minY
|
|
102
|
+
for row in rows(for: subviews, maxWidth: bounds.width) {
|
|
103
|
+
var x = bounds.minX
|
|
104
|
+
for item in row.items {
|
|
105
|
+
subviews[item.index].place(
|
|
106
|
+
at: CGPoint(x: x, y: y),
|
|
107
|
+
anchor: .topLeading,
|
|
108
|
+
proposal: ProposedViewSize(item.size)
|
|
109
|
+
)
|
|
110
|
+
x += item.size.width + gutter
|
|
111
|
+
}
|
|
112
|
+
y += row.height + gutter
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private func rows(for subviews: Subviews, maxWidth: CGFloat) -> [Row] {
|
|
117
|
+
var rows: [Row] = []
|
|
118
|
+
var current = Row()
|
|
119
|
+
for index in subviews.indices {
|
|
120
|
+
let size = subviews[index].sizeThatFits(ProposedViewSize(width: maxWidth, height: nil))
|
|
121
|
+
let widthIfAppended = current.items.isEmpty ? size.width : current.width + gutter + size.width
|
|
122
|
+
// Sub-point tolerance: spans are computed to land exactly on `maxWidth`.
|
|
123
|
+
if !current.items.isEmpty, widthIfAppended > maxWidth + 0.5 {
|
|
124
|
+
rows.append(current)
|
|
125
|
+
current = Row()
|
|
126
|
+
}
|
|
127
|
+
current.width = current.items.isEmpty ? size.width : current.width + gutter + size.width
|
|
128
|
+
current.height = max(current.height, size.height)
|
|
129
|
+
current.items.append((index, size))
|
|
130
|
+
}
|
|
131
|
+
if !current.items.isEmpty { rows.append(current) }
|
|
132
|
+
return rows
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import SwiftUI
|
|
3
|
+
|
|
4
|
+
/// Loader variants, mirroring Compose `LoaderType`.
|
|
5
|
+
public enum LoaderType {
|
|
6
|
+
case dot
|
|
7
|
+
case spinner
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// Native SwiftUI port of the Compose `Loader` component.
|
|
11
|
+
///
|
|
12
|
+
/// Mirrors Compose `Loader(type:, tintColor:, modifier:)` which delegates to
|
|
13
|
+
/// `DotLoader` / `SpinnerLoader`. Both variants render Lottie animations
|
|
14
|
+
/// (`files/dot_loading` and `files/lottie_circle_loader`) and optionally tint
|
|
15
|
+
/// the fill/stroke. The tint is reproduced here by masking a solid color rect
|
|
16
|
+
/// with the (monochrome) Lottie animation.
|
|
17
|
+
public struct Loader: View {
|
|
18
|
+
private let type: LoaderType
|
|
19
|
+
private let tintColor: Color?
|
|
20
|
+
|
|
21
|
+
public init(type: LoaderType = .dot, tintColor: Color? = nil) {
|
|
22
|
+
self.type = type
|
|
23
|
+
self.tintColor = tintColor
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public var body: some View {
|
|
27
|
+
switch type {
|
|
28
|
+
case .dot:
|
|
29
|
+
DotLoader(tintColor: tintColor)
|
|
30
|
+
case .spinner:
|
|
31
|
+
SpinnerLoader(tintColor: tintColor)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Looping dot animation. Compose size: 52 x 18.
|
|
37
|
+
public struct DotLoader: View {
|
|
38
|
+
private let tintColor: Color?
|
|
39
|
+
|
|
40
|
+
public init(tintColor: Color? = nil) {
|
|
41
|
+
self.tintColor = tintColor
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public var body: some View {
|
|
45
|
+
TintedLottie(name: "dot_loading", tintColor: tintColor)
|
|
46
|
+
.frame(width: 52, height: 18)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Looping circular spinner. Compose size: 24 x 24.
|
|
51
|
+
public struct SpinnerLoader: View {
|
|
52
|
+
private let tintColor: Color?
|
|
53
|
+
|
|
54
|
+
public init(tintColor: Color? = nil) {
|
|
55
|
+
self.tintColor = tintColor
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public var body: some View {
|
|
59
|
+
TintedLottie(name: "lottie_circle_loader", tintColor: tintColor)
|
|
60
|
+
.frame(width: 24, height: 24)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Horizontal determinate progress bar.
|
|
65
|
+
///
|
|
66
|
+
/// Mirrors Compose `ProgressBar(percent:, color:, modifier:)`:
|
|
67
|
+
/// - `percent` clamped to 0...100, animated with a 200ms ease.
|
|
68
|
+
/// - track = theme background default (`0xFFF2F2F6`), fill = `color` or primary (pink_03).
|
|
69
|
+
/// - height 4, corner radius `Radius.XXS`, fills max width.
|
|
70
|
+
public struct ProgressBar: View {
|
|
71
|
+
@Environment(\.appTheme) private var theme
|
|
72
|
+
|
|
73
|
+
private let percent: CGFloat
|
|
74
|
+
private let color: Color?
|
|
75
|
+
|
|
76
|
+
public init(percent: CGFloat = 0, color: Color? = nil) {
|
|
77
|
+
self.percent = percent
|
|
78
|
+
self.color = color
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private var clampedFraction: CGFloat {
|
|
82
|
+
min(max(percent, 0), 100) / 100
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public var body: some View {
|
|
86
|
+
GeometryReader { proxy in
|
|
87
|
+
ZStack(alignment: .leading) {
|
|
88
|
+
RoundedRectangle(cornerRadius: Radius.XXS)
|
|
89
|
+
.fill(theme.colors.background.default)
|
|
90
|
+
RoundedRectangle(cornerRadius: Radius.XXS)
|
|
91
|
+
.fill(color ?? theme.colors.primary)
|
|
92
|
+
.frame(width: proxy.size.width * clampedFraction)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
.frame(height: 4)
|
|
96
|
+
.animation(.easeInOut(duration: 0.2), value: clampedFraction)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Renders a looping Lottie animation, optionally recolored via a mask.
|
|
101
|
+
private struct TintedLottie: View {
|
|
102
|
+
let name: String
|
|
103
|
+
let tintColor: Color?
|
|
104
|
+
|
|
105
|
+
var body: some View {
|
|
106
|
+
if let tintColor = tintColor {
|
|
107
|
+
tintColor
|
|
108
|
+
.mask(LottieView(name: name, loopMode: .loop))
|
|
109
|
+
} else {
|
|
110
|
+
LottieView(name: name, loopMode: .loop)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//
|
|
2
|
+
// LottieView.swift
|
|
3
|
+
// MoMoPlatform
|
|
4
|
+
//
|
|
5
|
+
// Created by thanhdat on 16/01/2023.
|
|
6
|
+
// Copyright © 2023 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import SwiftUI
|
|
10
|
+
import Lottie
|
|
11
|
+
|
|
12
|
+
struct LottieView: UIViewRepresentable {
|
|
13
|
+
var name: String = ""
|
|
14
|
+
var url: String?
|
|
15
|
+
var loopMode: LottieLoopMode = .playOnce
|
|
16
|
+
var contentMode : UIView.ContentMode = .scaleAspectFit
|
|
17
|
+
var onDone: (() -> Void)?
|
|
18
|
+
|
|
19
|
+
// Add static cache for animations
|
|
20
|
+
private static var animationCache = NSCache<NSString, LottieAnimation>()
|
|
21
|
+
|
|
22
|
+
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
|
|
23
|
+
let view = UIView(frame: .zero)
|
|
24
|
+
|
|
25
|
+
// Configure Lottie with optimized settings
|
|
26
|
+
let configuration = LottieConfiguration(
|
|
27
|
+
renderingEngine: .coreAnimation,
|
|
28
|
+
decodingStrategy: .dictionaryBased
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
let animationView = LottieAnimationView(configuration: configuration)
|
|
32
|
+
|
|
33
|
+
if let url = url {
|
|
34
|
+
// Use cached animation if available
|
|
35
|
+
if let cachedAnimation = Self.animationCache.object(forKey: url as NSString) {
|
|
36
|
+
animationView.animation = cachedAnimation
|
|
37
|
+
setupAnimationView(animationView, view)
|
|
38
|
+
} else {
|
|
39
|
+
LottieAnimation.loadedFrom(url: URL(string: url)!, closure: { animation in
|
|
40
|
+
if let animation = animation {
|
|
41
|
+
Self.animationCache.setObject(animation, forKey: url as NSString)
|
|
42
|
+
animationView.animation = animation
|
|
43
|
+
setupAnimationView(animationView, view)
|
|
44
|
+
}
|
|
45
|
+
}, animationCache: nil)
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
// Use cached animation if available
|
|
49
|
+
if let cachedAnimation = Self.animationCache.object(forKey: name as NSString) {
|
|
50
|
+
animationView.animation = cachedAnimation
|
|
51
|
+
setupAnimationView(animationView, view)
|
|
52
|
+
} else if let animation = LottieAnimation.named(name) {
|
|
53
|
+
Self.animationCache.setObject(animation, forKey: name as NSString)
|
|
54
|
+
animationView.animation = animation
|
|
55
|
+
setupAnimationView(animationView, view)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return view
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private func setupAnimationView(_ animationView: LottieAnimationView, _ containerView: UIView) {
|
|
63
|
+
animationView.contentMode = contentMode
|
|
64
|
+
animationView.loopMode = loopMode
|
|
65
|
+
animationView.backgroundBehavior = loopMode == .loop ? .pauseAndRestore : .pause
|
|
66
|
+
|
|
67
|
+
// Optimize rendering
|
|
68
|
+
animationView.shouldRasterizeWhenIdle = true
|
|
69
|
+
|
|
70
|
+
animationView.translatesAutoresizingMaskIntoConstraints = false
|
|
71
|
+
containerView.addSubview(animationView)
|
|
72
|
+
|
|
73
|
+
NSLayoutConstraint.activate([
|
|
74
|
+
animationView.widthAnchor.constraint(equalTo: containerView.widthAnchor),
|
|
75
|
+
animationView.heightAnchor.constraint(equalTo: containerView.heightAnchor)
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
animationView.play { finished in
|
|
79
|
+
if finished {
|
|
80
|
+
onDone?()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {}
|
|
86
|
+
}
|
|
@@ -8,9 +8,13 @@ typealias SwiftButton = SwiftUI.Button
|
|
|
8
8
|
public struct KeyboardButton: View {
|
|
9
9
|
// MARK: Lifecycle
|
|
10
10
|
|
|
11
|
-
public init(_ key: String, action: @escaping (_ key: String)->Void) {
|
|
11
|
+
public init(_ key: String, action: @escaping (_ key: String)->Void, width: CGFloat = .infinity, height: CGFloat = 48, color: Color = Colors.card, textColor: Color = Colors.text) {
|
|
12
12
|
self.key = key
|
|
13
13
|
self.action = action
|
|
14
|
+
self.width = width
|
|
15
|
+
self.height = height
|
|
16
|
+
self.color = color
|
|
17
|
+
self.textColor = textColor
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
// MARK: Public
|
|
@@ -18,10 +22,10 @@ public struct KeyboardButton: View {
|
|
|
18
22
|
public var body: some View {
|
|
19
23
|
SwiftUI.Button(action: { self.action(key) }) {
|
|
20
24
|
HStack {
|
|
21
|
-
Text(key).font(.system(size: 24, weight: .bold))
|
|
22
|
-
}.frame(maxWidth:
|
|
23
|
-
.background(
|
|
24
|
-
.foregroundColor(
|
|
25
|
+
Text(key).font(.system(size: 24, weight: .bold)).ignoreBoldTextSetting()
|
|
26
|
+
}.frame(maxWidth: CGFloat(width), minHeight: height)
|
|
27
|
+
.background(color)
|
|
28
|
+
.foregroundColor(textColor)
|
|
25
29
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
26
30
|
}
|
|
27
31
|
}
|
|
@@ -30,4 +34,8 @@ public struct KeyboardButton: View {
|
|
|
30
34
|
|
|
31
35
|
var key: String = ""
|
|
32
36
|
var action: (_ key: String)->Void
|
|
37
|
+
var width: CGFloat
|
|
38
|
+
var height: CGFloat
|
|
39
|
+
var color: Color
|
|
40
|
+
var textColor: Color
|
|
33
41
|
}
|
|
@@ -119,7 +119,7 @@ public struct OTPKeyboard: View {
|
|
|
119
119
|
private struct ValueView: View {
|
|
120
120
|
// MARK: Lifecycle
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
init(_ value: String, valueLength: Int) {
|
|
123
123
|
self.value = value
|
|
124
124
|
self.valueLength = valueLength
|
|
125
125
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PaginationDot.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// Native SwiftUI port of Compose's PaginationDot.
|
|
6
|
+
// An active dot is a 12x4 pill in `activeColor`; inactive dots are 4x4
|
|
7
|
+
// rounded squares in the pressed-background token.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import SwiftUI
|
|
11
|
+
|
|
12
|
+
// MARK: - Dot
|
|
13
|
+
|
|
14
|
+
/// A single pagination dot, mirroring Compose's `Dot` composable.
|
|
15
|
+
public struct PaginationDotItem: View {
|
|
16
|
+
@Environment(\.appTheme) private var theme
|
|
17
|
+
|
|
18
|
+
private let active: Bool
|
|
19
|
+
private let activeColor: Color
|
|
20
|
+
|
|
21
|
+
public init(active: Bool = false, activeColor: Color) {
|
|
22
|
+
self.active = active
|
|
23
|
+
self.activeColor = activeColor
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public var body: some View {
|
|
27
|
+
if active {
|
|
28
|
+
RoundedRectangle(cornerRadius: 4)
|
|
29
|
+
.fill(activeColor)
|
|
30
|
+
.frame(width: 12, height: 4)
|
|
31
|
+
} else {
|
|
32
|
+
RoundedRectangle(cornerRadius: 4)
|
|
33
|
+
.fill(theme.colors.background.pressed)
|
|
34
|
+
.frame(width: 4, height: 4)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// MARK: - PaginationDot
|
|
40
|
+
|
|
41
|
+
/// Mirrors Compose's `PaginationDot(activeIndex, dataLength)`.
|
|
42
|
+
public struct PaginationDot: View {
|
|
43
|
+
@Environment(\.appTheme) private var theme
|
|
44
|
+
|
|
45
|
+
private let activeIndex: Int
|
|
46
|
+
private let dataLength: Int
|
|
47
|
+
|
|
48
|
+
public init(activeIndex: Int = 0, dataLength: Int = 3) {
|
|
49
|
+
self.activeIndex = activeIndex
|
|
50
|
+
self.dataLength = dataLength
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public var body: some View {
|
|
54
|
+
HStack(spacing: 0) {
|
|
55
|
+
ForEach(0..<max(dataLength, 0), id: \.self) { i in
|
|
56
|
+
PaginationDotItem(active: i == activeIndex, activeColor: theme.colors.primary)
|
|
57
|
+
.padding(.trailing, i != dataLength - 1 ? Spacing.XS : 0)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PaginationNumber.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// Native SwiftUI port of Compose's PaginationNumber.
|
|
6
|
+
// Renders "current/total" text on a translucent black rounded chip.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import SwiftUI
|
|
10
|
+
|
|
11
|
+
// MARK: - PaginationNumber
|
|
12
|
+
|
|
13
|
+
/// Mirrors Compose's `PaginationNumber(activeIndex, dataLength)`.
|
|
14
|
+
public struct PaginationNumber: View {
|
|
15
|
+
private let activeIndex: Int
|
|
16
|
+
private let dataLength: Int
|
|
17
|
+
|
|
18
|
+
public init(activeIndex: Int = 0, dataLength: Int = 2) {
|
|
19
|
+
self.activeIndex = activeIndex
|
|
20
|
+
self.dataLength = dataLength
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public var body: some View {
|
|
24
|
+
MomoText(
|
|
25
|
+
"\(1 + activeIndex)/\(dataLength)",
|
|
26
|
+
typography: .labelDefaultMedium,
|
|
27
|
+
color: Colors.black01
|
|
28
|
+
)
|
|
29
|
+
.padding(.horizontal, Spacing.S)
|
|
30
|
+
.background(
|
|
31
|
+
RoundedRectangle(cornerRadius: Radius.L)
|
|
32
|
+
.fill(Color.black.opacity(0.2)) // Color(0x33000000)
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PaginationScroll.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// Native SwiftUI port of Compose's PaginationScroll.
|
|
6
|
+
// Wraps arbitrary horizontally-scrollable content and renders a
|
|
7
|
+
// progress-bar style indicator whose position tracks the scroll offset.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import SwiftUI
|
|
11
|
+
|
|
12
|
+
// MARK: - Constants
|
|
13
|
+
|
|
14
|
+
private let INDICATOR_WIDTH: CGFloat = 24
|
|
15
|
+
private let PROGRESS_WIDTH: CGFloat = 72
|
|
16
|
+
|
|
17
|
+
/// Memoized offset calculation, mirroring Compose's `calculateOffset`.
|
|
18
|
+
private func calculateOffset(currentPosition: CGFloat, maxBound: CGFloat) -> CGFloat {
|
|
19
|
+
guard maxBound > 0 else { return 0 }
|
|
20
|
+
return (PROGRESS_WIDTH - INDICATOR_WIDTH) * (currentPosition / maxBound)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// MARK: - PaginationScroll
|
|
24
|
+
|
|
25
|
+
/// Mirrors Compose's `PaginationScroll(modifier, content)`.
|
|
26
|
+
///
|
|
27
|
+
/// The trailing-closure `content` is laid out in a horizontal scroll view and
|
|
28
|
+
/// the indicator below reflects the current scroll progress.
|
|
29
|
+
public struct PaginationScroll<Content: View>: View {
|
|
30
|
+
@Environment(\.appTheme) private var theme
|
|
31
|
+
|
|
32
|
+
private let content: Content
|
|
33
|
+
|
|
34
|
+
@State private var scrollOffset: CGFloat = 0
|
|
35
|
+
@State private var contentWidth: CGFloat = 0
|
|
36
|
+
@State private var viewportWidth: CGFloat = 0
|
|
37
|
+
|
|
38
|
+
public init(@ViewBuilder content: () -> Content) {
|
|
39
|
+
self.content = content()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private var maxBound: CGFloat {
|
|
43
|
+
max(contentWidth - viewportWidth, 0)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private var indicatorOffset: CGFloat {
|
|
47
|
+
calculateOffset(currentPosition: scrollOffset, maxBound: maxBound)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public var body: some View {
|
|
51
|
+
VStack(spacing: 0) {
|
|
52
|
+
ScrollView(.horizontal, showsIndicators: false) {
|
|
53
|
+
HStack(spacing: 0) {
|
|
54
|
+
content
|
|
55
|
+
}
|
|
56
|
+
.background(
|
|
57
|
+
GeometryReader { proxy in
|
|
58
|
+
Color.clear
|
|
59
|
+
.preference(
|
|
60
|
+
key: ScrollOffsetKey.self,
|
|
61
|
+
value: -proxy.frame(in: .named("paginationScroll")).minX
|
|
62
|
+
)
|
|
63
|
+
.onAppear { contentWidth = proxy.size.width }
|
|
64
|
+
.onChange(of: proxy.size.width) { contentWidth = $0 }
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
.coordinateSpace(name: "paginationScroll")
|
|
69
|
+
.background(
|
|
70
|
+
GeometryReader { viewport in
|
|
71
|
+
Color.clear
|
|
72
|
+
.onAppear { viewportWidth = viewport.size.width }
|
|
73
|
+
.onChange(of: viewport.size.width) { viewportWidth = $0 }
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
.onPreferenceChange(ScrollOffsetKey.self) { scrollOffset = max($0, 0) }
|
|
77
|
+
.padding(.bottom, Spacing.L)
|
|
78
|
+
|
|
79
|
+
// Progress indicator
|
|
80
|
+
ZStack(alignment: .leading) {
|
|
81
|
+
RoundedRectangle(cornerRadius: Spacing.XS)
|
|
82
|
+
.fill(theme.colors.background.pressed)
|
|
83
|
+
.frame(width: PROGRESS_WIDTH, height: 4)
|
|
84
|
+
|
|
85
|
+
RoundedRectangle(cornerRadius: Spacing.XS)
|
|
86
|
+
.fill(theme.colors.primary)
|
|
87
|
+
.frame(width: INDICATOR_WIDTH, height: 4)
|
|
88
|
+
.offset(x: indicatorOffset)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// MARK: - Preference key
|
|
95
|
+
|
|
96
|
+
private struct ScrollOffsetKey: PreferenceKey {
|
|
97
|
+
static var defaultValue: CGFloat = 0
|
|
98
|
+
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
99
|
+
value = nextValue()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PaginationWhiteDot.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// Native SwiftUI port of Compose's PaginationWhiteDot.
|
|
6
|
+
// Same dot layout as PaginationDot, but the active color is white
|
|
7
|
+
// (black_01) and the row sits on a translucent black rounded surface.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import SwiftUI
|
|
11
|
+
|
|
12
|
+
// MARK: - PaginationWhiteDot
|
|
13
|
+
|
|
14
|
+
/// Mirrors Compose's `PaginationWhiteDot(activeIndex, dataLength)`.
|
|
15
|
+
public struct PaginationWhiteDot: View {
|
|
16
|
+
private let activeIndex: Int
|
|
17
|
+
private let dataLength: Int
|
|
18
|
+
|
|
19
|
+
public init(activeIndex: Int = 0, dataLength: Int = 3) {
|
|
20
|
+
self.activeIndex = activeIndex
|
|
21
|
+
self.dataLength = dataLength
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public var body: some View {
|
|
25
|
+
HStack(spacing: 0) {
|
|
26
|
+
ForEach(0..<max(dataLength, 0), id: \.self) { i in
|
|
27
|
+
PaginationDotItem(active: i == activeIndex, activeColor: Colors.black01)
|
|
28
|
+
.padding(.trailing, i != dataLength - 1 ? Spacing.XS : 0)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
.padding(Spacing.XS)
|
|
32
|
+
.background(
|
|
33
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
34
|
+
.fill(Color.black.opacity(0.2)) // Color(0x33000000)
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|