@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,453 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Navigator.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's Navigator + DynamicScreenRegistry.
|
|
6
|
+
// Screens are registered by Int id; the NavigationStack path stores routes
|
|
7
|
+
// by id and looks up content from the registry on render.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import SwiftUI
|
|
11
|
+
import Combine
|
|
12
|
+
|
|
13
|
+
// MARK: - Routes
|
|
14
|
+
|
|
15
|
+
public struct DynamicScreenRoute: Hashable, Identifiable {
|
|
16
|
+
public let id: Int
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public struct DynamicDialogRoute: Hashable, Identifiable {
|
|
20
|
+
public let id: Int
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// MARK: - Registry
|
|
24
|
+
|
|
25
|
+
public struct DynamicScreen {
|
|
26
|
+
public let id: Int
|
|
27
|
+
public let name: String
|
|
28
|
+
public let content: () -> AnyView
|
|
29
|
+
public var options: NavigationOptions?
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public final class DynamicScreenRegistry {
|
|
33
|
+
private var screens: [Int: DynamicScreen] = [:]
|
|
34
|
+
private var idCounter: Int = 1
|
|
35
|
+
|
|
36
|
+
public init() {}
|
|
37
|
+
|
|
38
|
+
@discardableResult
|
|
39
|
+
public func register(
|
|
40
|
+
screenName: String = "",
|
|
41
|
+
content: @escaping () -> AnyView,
|
|
42
|
+
options: NavigationOptions? = nil
|
|
43
|
+
) -> DynamicScreenRoute {
|
|
44
|
+
let id = idCounter
|
|
45
|
+
idCounter += 1
|
|
46
|
+
screens[id] = DynamicScreen(id: id, name: screenName, content: content, options: options)
|
|
47
|
+
return DynamicScreenRoute(id: id)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public func unregister(id: Int) {
|
|
51
|
+
screens.removeValue(forKey: id)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public func unregisterAll() {
|
|
55
|
+
screens.removeAll()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public func getScreen(id: Int) -> DynamicScreen? { screens[id] }
|
|
59
|
+
|
|
60
|
+
public func latest() -> DynamicScreen? {
|
|
61
|
+
screens.max(by: { $0.key < $1.key })?.value
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public func setOptions(id: Int, options: NavigationOptions) {
|
|
65
|
+
screens[id]?.options = options
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// MARK: - Overplay
|
|
70
|
+
|
|
71
|
+
public enum OverplayType {
|
|
72
|
+
case modal, bottomSheet, snackBar
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public struct OverplayItem: Identifiable {
|
|
76
|
+
public let id = UUID()
|
|
77
|
+
public let type: OverplayType
|
|
78
|
+
public let content: () -> AnyView
|
|
79
|
+
public let barrierDismissible: Bool
|
|
80
|
+
public let onDismiss: (() -> Void)?
|
|
81
|
+
// Bottom-sheet params (mirrors Compose's OverplayComponentParams.BottomSheet).
|
|
82
|
+
public var isSurface: Bool = false
|
|
83
|
+
public var header: BottomSheetHeader? = nil
|
|
84
|
+
// Snack-bar payload (mirrors Compose's SnackBar value).
|
|
85
|
+
public var snackBar: SnackBar? = nil
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// MARK: - Navigator
|
|
89
|
+
|
|
90
|
+
@available(iOS 16.0, *)
|
|
91
|
+
public final class Navigator: ObservableObject {
|
|
92
|
+
@Published public var path: [DynamicScreenRoute] = [] {
|
|
93
|
+
// Disposal hangs off the path so every route that leaves the stack — pop,
|
|
94
|
+
// replace, reset — is released the same way, after its exit transition.
|
|
95
|
+
didSet { disposeScreens(droppedFrom: oldValue) }
|
|
96
|
+
}
|
|
97
|
+
@Published public var presented: DynamicDialogRoute?
|
|
98
|
+
@Published public var overplay: OverplayItem?
|
|
99
|
+
|
|
100
|
+
/// Owned per-Navigator (mirrors Compose's `remember { DynamicScreenRegistry() }` +
|
|
101
|
+
/// disposal): screens registered by this navigator are released via ARC when the
|
|
102
|
+
/// navigator itself deallocates, instead of accumulating in a process-wide singleton.
|
|
103
|
+
public let registry = DynamicScreenRegistry()
|
|
104
|
+
|
|
105
|
+
/// Animated-close handler registered by the active overplay component
|
|
106
|
+
/// (mirrors Compose's `OverplayComponentRegistry.bindClose`). When set, a
|
|
107
|
+
/// dismiss request runs the component's exit animation before the item is
|
|
108
|
+
/// actually removed via `finishOverplayDismiss`.
|
|
109
|
+
private var overplayDismissHandler: (() -> Void)?
|
|
110
|
+
|
|
111
|
+
public private(set) var rootRoute: DynamicScreenRoute
|
|
112
|
+
public var composeApi: KitComposeApi?
|
|
113
|
+
|
|
114
|
+
public init(
|
|
115
|
+
initialScreenName: String = "",
|
|
116
|
+
initialContent: @escaping () -> AnyView,
|
|
117
|
+
options: NavigationOptions? = nil,
|
|
118
|
+
composeApi: KitComposeApi? = nil
|
|
119
|
+
) {
|
|
120
|
+
self.composeApi = composeApi
|
|
121
|
+
self.rootRoute = registry.register(
|
|
122
|
+
screenName: initialScreenName,
|
|
123
|
+
content: initialContent,
|
|
124
|
+
options: options
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// MARK: Stack
|
|
129
|
+
|
|
130
|
+
public func push<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
|
|
131
|
+
let route = registry.register(
|
|
132
|
+
screenName: options?.screenName ?? "",
|
|
133
|
+
content: { AnyView(content()) },
|
|
134
|
+
options: options
|
|
135
|
+
)
|
|
136
|
+
path.append(route)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public func push<V: View>(
|
|
140
|
+
screenName: String,
|
|
141
|
+
@ViewBuilder _ content: @escaping () -> V,
|
|
142
|
+
options: NavigationOptions? = nil
|
|
143
|
+
) {
|
|
144
|
+
let route = registry.register(
|
|
145
|
+
screenName: screenName,
|
|
146
|
+
content: { AnyView(content()) },
|
|
147
|
+
options: options
|
|
148
|
+
)
|
|
149
|
+
path.append(route)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public func replace<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
|
|
153
|
+
guard !path.isEmpty else { return }
|
|
154
|
+
path.removeLast()
|
|
155
|
+
push(content, options: options)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public func replace<V: View>(
|
|
159
|
+
screenName: String,
|
|
160
|
+
@ViewBuilder _ content: @escaping () -> V,
|
|
161
|
+
options: NavigationOptions? = nil
|
|
162
|
+
) {
|
|
163
|
+
guard !path.isEmpty else { return }
|
|
164
|
+
path.removeLast()
|
|
165
|
+
push(screenName: screenName, content, options: options)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public func reset<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
|
|
169
|
+
let previousRoot = rootRoute.id
|
|
170
|
+
path.removeAll()
|
|
171
|
+
let route = registry.register(
|
|
172
|
+
screenName: options?.screenName ?? "",
|
|
173
|
+
content: { AnyView(content()) },
|
|
174
|
+
options: options
|
|
175
|
+
)
|
|
176
|
+
rootRoute = route
|
|
177
|
+
disposeScreens([previousRoot])
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public func reset<V: View>(
|
|
181
|
+
screenName: String,
|
|
182
|
+
@ViewBuilder _ content: @escaping () -> V,
|
|
183
|
+
options: NavigationOptions? = nil
|
|
184
|
+
) {
|
|
185
|
+
let previousRoot = rootRoute.id
|
|
186
|
+
path.removeAll()
|
|
187
|
+
let route = registry.register(
|
|
188
|
+
screenName: screenName,
|
|
189
|
+
content: { AnyView(content()) },
|
|
190
|
+
options: options
|
|
191
|
+
)
|
|
192
|
+
rootRoute = route
|
|
193
|
+
disposeScreens([previousRoot])
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public func pop(_ count: Int = 1, callback: (() -> Void)? = nil) {
|
|
197
|
+
popStep(remaining: count, callback: callback)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// One `pop` iteration. Compose runs the loop in a coroutine and suspends over the
|
|
201
|
+
/// overplay's exit animation before the next step; the same wait is needed here, or
|
|
202
|
+
/// a `pop(2)` (prevent-back: close the popup, then leave) would spend both steps on
|
|
203
|
+
/// the overplay that is still animating out and never pop the screen.
|
|
204
|
+
private func popStep(remaining: Int, callback: (() -> Void)?) {
|
|
205
|
+
guard remaining > 0 else {
|
|
206
|
+
callback?()
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if overplay?.type == .snackBar {
|
|
211
|
+
// Mirrors Kotlin: a snackbar doesn't block back-navigation, so popping
|
|
212
|
+
// while one is showing pops the screen underneath and clears the
|
|
213
|
+
// snackbar immediately, without playing its dismiss animation.
|
|
214
|
+
overplayDismissHandler = nil
|
|
215
|
+
overplay = nil
|
|
216
|
+
guard popScreen() else {
|
|
217
|
+
callback?()
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
popStep(remaining: remaining - 1, callback: callback)
|
|
221
|
+
} else if overplay != nil {
|
|
222
|
+
hideOverplay()
|
|
223
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + Self.overplayDismissDelay) { [weak self] in
|
|
224
|
+
guard let self else { return }
|
|
225
|
+
// The component reports back through `finishOverplayDismiss` once its exit
|
|
226
|
+
// animation ends; clear it here if it never did, so the step is not wasted.
|
|
227
|
+
if self.overplay != nil { self.finishOverplayDismiss() }
|
|
228
|
+
self.popStep(remaining: remaining - 1, callback: callback)
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
guard popScreen() else {
|
|
232
|
+
callback?()
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
popStep(remaining: remaining - 1, callback: callback)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// Pops the topmost presented screen or stack entry. Returns `false` (and requests
|
|
240
|
+
/// a host dismiss) when there is nothing left to pop.
|
|
241
|
+
@discardableResult
|
|
242
|
+
private func popScreen() -> Bool {
|
|
243
|
+
if presented != nil {
|
|
244
|
+
let dismissedId = presented?.id
|
|
245
|
+
presented = nil
|
|
246
|
+
if let id = dismissedId { disposeScreens([id]) }
|
|
247
|
+
return true
|
|
248
|
+
} else if !path.isEmpty {
|
|
249
|
+
path.removeLast()
|
|
250
|
+
return true
|
|
251
|
+
} else {
|
|
252
|
+
composeApi?.requestCallback(funcName: "dismiss", params: nil, onResponse: nil)
|
|
253
|
+
return false
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public func popToRoot() {
|
|
258
|
+
path.removeAll()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// The entry a back action applies to — mirrors Compose's `currentScreen()`
|
|
262
|
+
/// (`navController.currentBackStackEntry`): a presented screen sits on top of
|
|
263
|
+
/// the stack, otherwise it is the last pushed screen, otherwise the root.
|
|
264
|
+
private func currentScreen() -> DynamicScreen? {
|
|
265
|
+
registry.getScreen(id: presented?.id ?? path.last?.id ?? rootRoute.id)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public func onBackSafe(callback: (() -> Void)? = nil) {
|
|
269
|
+
let latest = currentScreen()
|
|
270
|
+
if let onBack = latest?.options?.onBackHandler {
|
|
271
|
+
onBack()
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
if let preventBack = latest?.options?.headerBackProps?.preventBack {
|
|
275
|
+
showModal {
|
|
276
|
+
PopupNotify(
|
|
277
|
+
props: PopupNotifyProps(
|
|
278
|
+
image: preventBack.image,
|
|
279
|
+
title: preventBack.title,
|
|
280
|
+
description: preventBack.description,
|
|
281
|
+
error: preventBack.error,
|
|
282
|
+
primary: PopupAction(
|
|
283
|
+
title: preventBack.primary?.title ?? "",
|
|
284
|
+
onPress: { [weak self] in
|
|
285
|
+
preventBack.primary?.onPress?()
|
|
286
|
+
// Close the popup, then leave the screen (matches Compose's pop(2)).
|
|
287
|
+
self?.pop(2, callback: callback)
|
|
288
|
+
}
|
|
289
|
+
),
|
|
290
|
+
secondary: preventBack.secondary.map { secondary in
|
|
291
|
+
PopupAction(
|
|
292
|
+
title: secondary.title,
|
|
293
|
+
onPress: { [weak self] in
|
|
294
|
+
secondary.onPress?()
|
|
295
|
+
self?.hideOverplay()
|
|
296
|
+
}
|
|
297
|
+
)
|
|
298
|
+
},
|
|
299
|
+
buttonDirection: preventBack.buttonDirection,
|
|
300
|
+
onIconClose: { [weak self] in
|
|
301
|
+
preventBack.onIconClose?()
|
|
302
|
+
self?.hideOverplay()
|
|
303
|
+
},
|
|
304
|
+
isShowCloseIcon: preventBack.isShowCloseIcon
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
pop(1, callback: callback)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// MARK: Modal-style screen (vertical slide)
|
|
314
|
+
|
|
315
|
+
public func present<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
|
|
316
|
+
let route = registry.register(
|
|
317
|
+
screenName: options?.screenName ?? "",
|
|
318
|
+
content: { AnyView(content()) },
|
|
319
|
+
options: options
|
|
320
|
+
)
|
|
321
|
+
presented = DynamicDialogRoute(id: route.id)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
public func present<V: View>(
|
|
325
|
+
screenName: String,
|
|
326
|
+
@ViewBuilder _ content: @escaping () -> V,
|
|
327
|
+
options: NavigationOptions? = nil
|
|
328
|
+
) {
|
|
329
|
+
let route = registry.register(
|
|
330
|
+
screenName: screenName,
|
|
331
|
+
content: { AnyView(content()) },
|
|
332
|
+
options: options
|
|
333
|
+
)
|
|
334
|
+
presented = DynamicDialogRoute(id: route.id)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
public func dismiss() {
|
|
338
|
+
if let id = presented?.id {
|
|
339
|
+
registry.unregister(id: id)
|
|
340
|
+
}
|
|
341
|
+
presented = nil
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// MARK: Overplay
|
|
345
|
+
|
|
346
|
+
public func showModal<V: View>(
|
|
347
|
+
@ViewBuilder _ content: @escaping () -> V,
|
|
348
|
+
barrierDismissible: Bool = true,
|
|
349
|
+
onDismiss: (() -> Void)? = nil
|
|
350
|
+
) {
|
|
351
|
+
overplayDismissHandler = nil
|
|
352
|
+
overplay = OverplayItem(
|
|
353
|
+
type: .modal,
|
|
354
|
+
content: { AnyView(content()) },
|
|
355
|
+
barrierDismissible: barrierDismissible,
|
|
356
|
+
onDismiss: onDismiss
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
public func showBottomSheet<V: View>(
|
|
361
|
+
@ViewBuilder _ content: @escaping () -> V,
|
|
362
|
+
isSurface: Bool = false,
|
|
363
|
+
barrierDismissible: Bool = true,
|
|
364
|
+
header: BottomSheetHeader? = nil,
|
|
365
|
+
onDismiss: (() -> Void)? = nil
|
|
366
|
+
) {
|
|
367
|
+
overplayDismissHandler = nil
|
|
368
|
+
overplay = OverplayItem(
|
|
369
|
+
type: .bottomSheet,
|
|
370
|
+
content: { AnyView(content()) },
|
|
371
|
+
barrierDismissible: barrierDismissible,
|
|
372
|
+
onDismiss: onDismiss,
|
|
373
|
+
isSurface: isSurface,
|
|
374
|
+
header: header
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/// Mirrors Compose's `showSnackBar(snackBar:)`.
|
|
379
|
+
public func showSnackBar(_ snackBar: SnackBar, onDismiss: (() -> Void)? = nil) {
|
|
380
|
+
overplayDismissHandler = nil
|
|
381
|
+
overplay = OverplayItem(
|
|
382
|
+
type: .snackBar,
|
|
383
|
+
content: { AnyView(EmptyView()) },
|
|
384
|
+
barrierDismissible: false,
|
|
385
|
+
onDismiss: onDismiss,
|
|
386
|
+
snackBar: snackBar
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// MARK: Overplay dismissal
|
|
391
|
+
|
|
392
|
+
/// Registered by the active overplay component so external dismiss requests
|
|
393
|
+
/// run its exit animation (mirrors Compose's `bindClose`).
|
|
394
|
+
public func registerOverplayDismiss(_ handler: @escaping () -> Void) {
|
|
395
|
+
overplayDismissHandler = handler
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/// Removes the overplay and fires its `onDismiss` (mirrors Compose's
|
|
399
|
+
/// `hardClearAfterDismiss`). Called by a component once its exit animation
|
|
400
|
+
/// has completed.
|
|
401
|
+
public func finishOverplayDismiss() {
|
|
402
|
+
let dismissCallback = overplay?.onDismiss
|
|
403
|
+
overplayDismissHandler = nil
|
|
404
|
+
overplay = nil
|
|
405
|
+
dismissCallback?()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public func hideOverplay() {
|
|
409
|
+
if let close = overplayDismissHandler {
|
|
410
|
+
// Component runs its exit animation, then calls finishOverplayDismiss.
|
|
411
|
+
overplayDismissHandler = nil
|
|
412
|
+
close()
|
|
413
|
+
} else {
|
|
414
|
+
finishOverplayDismiss()
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
public func hideSnackBar() {
|
|
419
|
+
if overplay?.type == .snackBar { hideOverplay() }
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// MARK: Screen disposal
|
|
423
|
+
|
|
424
|
+
/// Matches the `delay(300)` Compose waits in `dismissScreen` before unregistering,
|
|
425
|
+
/// with a little headroom for the pop transition.
|
|
426
|
+
private static let screenDisposeDelay: TimeInterval = 0.35
|
|
427
|
+
|
|
428
|
+
/// Longest overplay exit animation (the bottom sheet's), so a queued pop step runs
|
|
429
|
+
/// after the component has taken itself down.
|
|
430
|
+
private static let overplayDismissDelay: TimeInterval = 0.3
|
|
431
|
+
|
|
432
|
+
private func disposeScreens(droppedFrom previous: [DynamicScreenRoute]) {
|
|
433
|
+
let live = Set(path.map(\.id))
|
|
434
|
+
disposeScreens(previous.map(\.id).filter { !live.contains($0) })
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/// Screens are released only once the pop transition has played out: the outgoing
|
|
438
|
+
/// view is still on screen while it animates and the registry is what renders it —
|
|
439
|
+
/// dropping it right away blanks the screen mid-transition. Liveness is re-checked
|
|
440
|
+
/// when the timer fires, so a route pushed back before then keeps its screen.
|
|
441
|
+
private func disposeScreens(_ ids: [Int]) {
|
|
442
|
+
guard !ids.isEmpty else { return }
|
|
443
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + Self.screenDisposeDelay) { [weak self] in
|
|
444
|
+
guard let self else { return }
|
|
445
|
+
var live = Set(self.path.map(\.id))
|
|
446
|
+
live.insert(self.rootRoute.id)
|
|
447
|
+
if let presented = self.presented { live.insert(presented.id) }
|
|
448
|
+
for id in ids where !live.contains(id) {
|
|
449
|
+
self.registry.unregister(id: id)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@available(iOS 16.0, *)
|
|
4
|
+
public final class NavigatorManager {
|
|
5
|
+
public static let shared = NavigatorManager()
|
|
6
|
+
private init() {}
|
|
7
|
+
|
|
8
|
+
private var navigators: [Navigator] = []
|
|
9
|
+
|
|
10
|
+
public func push(_ navigator: Navigator) {
|
|
11
|
+
guard !navigators.contains(where: { $0 === navigator }) else { return }
|
|
12
|
+
navigators.append(navigator)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public func remove(_ navigator: Navigator) {
|
|
16
|
+
navigators.removeAll { $0 === navigator }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public func checkCanPop() -> Bool { !navigators.isEmpty }
|
|
20
|
+
|
|
21
|
+
public func pop() { navigators.last?.onBackSafe() }
|
|
22
|
+
}
|