@momo-kits/native-kits 0.167.1-duo.8-debug → 0.167.1-duo.9
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 +4 -1
- package/ios/Application/ApplicationEnvironment.swift +11 -0
- package/ios/Application/ComposeApi.swift +2 -1
- package/ios/Application/{Components.swift → LegacyHeader.swift} +6 -62
- package/ios/Application/Screen.swift +3 -2
- package/ios/Avatar/Avatar.swift +4 -3
- package/ios/Badge/Badge.swift +6 -4
- package/ios/Badge/BadgeDot.swift +1 -1
- package/ios/Badge/BadgeRibbon.swift +0 -1
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +12 -20
- package/ios/Carousel/Carousel.swift +29 -12
- package/ios/Checkbox/Checkbox.swift +7 -3
- package/ios/Chip/Chip.swift +5 -4
- package/ios/Collapse/Collapse.swift +70 -19
- package/ios/Colors+Radius+Spacing/Colors.swift +9 -0
- package/ios/DateTimePicker/DateTimePicker.swift +54 -20
- package/ios/DateTimePicker/WheelPicker.swift +103 -105
- package/ios/Divider/Divider.swift +37 -4
- package/ios/Extensions/Color++.swift +18 -0
- package/ios/Icon/Icon.swift +7 -1
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -34
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +160 -147
- package/ios/Input/InputPhoneNumber.swift +12 -9
- package/ios/Input/InputSearch.swift +102 -75
- package/ios/Input/InputTextArea.swift +128 -145
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +33 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +123 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +10 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +61 -9
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTab.swift +60 -34
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +11 -6
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
- package/ios/Navigation/Component/Header.swift +493 -0
- package/ios/Navigation/Component/HeaderBackground.swift +134 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +386 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +20 -23
- package/ios/{Application/Navigation/component → Navigation/Component}/SideBar.swift +49 -28
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
- package/ios/Navigation/HostBackGestureBridge.swift +57 -0
- package/ios/Navigation/Navigation.swift +190 -0
- package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +46 -41
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +65 -9
- package/ios/{Application → Navigation}/StackScreen.swift +236 -103
- package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
- package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
- package/ios/Pagination/PaginationScroll.swift +2 -2
- package/ios/Popup/PopupDisplay.swift +79 -159
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +97 -2
- package/ios/Popup/PopupNotify.swift +27 -55
- package/ios/Popup/PopupPromotion.swift +22 -50
- package/ios/PopupView/FullscreenPopup.swift +13 -19
- package/ios/PopupView/PopupView.swift +1 -1
- package/ios/PopupView/Utils++.swift +0 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +2 -1
- package/ios/Rating/Rating.swift +9 -1
- package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +6 -1
- package/ios/Steps/Steps.swift +8 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -0
- package/ios/Swipeable/SwipeCell.swift +10 -6
- package/ios/Switch/Switch.swift +22 -20
- package/ios/TabView/TabView.swift +102 -21
- package/ios/Tag/Tag.swift +15 -4
- package/ios/Template/Logo/Logo.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +41 -27
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/Text.swift +61 -19
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/package.json +1 -1
- package/build.gradle.kts +0 -11
- package/compose/build.gradle.kts +0 -204
- package/compose/build.gradle.kts.backup +0 -204
- package/compose/compose.podspec +0 -47
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -831
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -266
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -264
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -552
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -297
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -232
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -708
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -203
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -232
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -98
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -312
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -408
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -92
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -105
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -366
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -37
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -185
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -86
- package/gradle/libs.versions.toml +0 -69
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/gradle.properties +0 -26
- package/gradlew +0 -252
- package/gradlew.bat +0 -94
- package/ios/Application/HeaderRight.swift +0 -324
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -29
- package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -54
- package/ios/Application/Navigation/component/Header.swift +0 -183
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -84
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -280
- package/ios-demo/README.md +0 -120
- package/settings.gradle.kts +0 -64
- /package/ios/{Application/Layout → Layout}/ScreenLayout.swift +0 -0
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
- /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoBarCompression.swift +0 -0
- /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoTabBar.swift +0 -0
- /package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +0 -0
|
@@ -2,22 +2,15 @@
|
|
|
2
2
|
// SideBar.swift
|
|
3
3
|
// MoMoUIKits
|
|
4
4
|
//
|
|
5
|
-
// iPhone Duo: back and toolkit
|
|
6
|
-
//
|
|
5
|
+
// SwiftUI mirror of Compose's `navigation/component/SideBar.kt`. iPhone Duo: back and toolkit
|
|
6
|
+
// leave the header bar for a column in the side safe area, as the system bars do.
|
|
7
|
+
// Port of RN `SideBarButton.tsx` + `useSideBarItemStyle`.
|
|
7
8
|
//
|
|
8
9
|
|
|
9
10
|
import SwiftUI
|
|
10
11
|
|
|
11
12
|
private let sideBarIconSize: CGFloat = 24
|
|
12
13
|
|
|
13
|
-
/// Toolkit data when `headerRight` renders the kit toolkit; nil for a custom header right.
|
|
14
|
-
func toolkitHeaderRight(_ headerRight: (() -> any View)?) -> (data: HeaderRightData?, tintColor: Color?)? {
|
|
15
|
-
guard let view = headerRight?() else { return nil }
|
|
16
|
-
if let toolkit = view as? HeaderRight { return (toolkit.headerRight, toolkit.tintColor) }
|
|
17
|
-
if let toolkit = view as? NavigationHeaderRight { return (toolkit.headerRight, toolkit.tintColor) }
|
|
18
|
-
return nil
|
|
19
|
-
}
|
|
20
|
-
|
|
21
14
|
/// The back button and toolkit column, placed in window coordinates over the side strip.
|
|
22
15
|
struct HeaderSideBar: View {
|
|
23
16
|
let sideBar: SideBarLayout
|
|
@@ -28,6 +21,8 @@ struct HeaderSideBar: View {
|
|
|
28
21
|
let bottomInset: CGFloat
|
|
29
22
|
|
|
30
23
|
@Environment(\.screenLayout) private var layout
|
|
24
|
+
@Environment(\.applicationEnvironment) private var environment
|
|
25
|
+
@Environment(\.duoBarCompression) private var compression
|
|
31
26
|
@State private var columnHeight: CGFloat = 0
|
|
32
27
|
|
|
33
28
|
private var isRight: Bool { sideBar.edge == .right }
|
|
@@ -37,12 +32,20 @@ struct HeaderSideBar: View {
|
|
|
37
32
|
return max(min(sideBar.top, maxY), 0)
|
|
38
33
|
}
|
|
39
34
|
|
|
35
|
+
/// Full height, so the tab bar compression decision follows this screen's toolkit.
|
|
36
|
+
private var fullHeight: CGFloat {
|
|
37
|
+
let toolkitHeight = toolkit.map {
|
|
38
|
+
Spacing.M + toolkitColumnHeight($0.data ?? HeaderRightData(), context: environment.applicationContext)
|
|
39
|
+
}
|
|
40
|
+
return DuoMetrics.controlSize + (toolkitHeight ?? 0)
|
|
41
|
+
}
|
|
42
|
+
|
|
40
43
|
var body: some View {
|
|
41
44
|
VStack(alignment: isRight ? .leading : .trailing, spacing: Spacing.M) {
|
|
42
45
|
if let goBack {
|
|
43
46
|
SideBarButton(
|
|
44
47
|
icon: "arrow-back",
|
|
45
|
-
|
|
48
|
+
headerColor: headerColor,
|
|
46
49
|
accessibilityId: "btn_navigation_back",
|
|
47
50
|
action: goBack
|
|
48
51
|
)
|
|
@@ -51,11 +54,26 @@ struct HeaderSideBar: View {
|
|
|
51
54
|
Color.clear.frame(width: DuoMetrics.controlSize, height: DuoMetrics.controlSize)
|
|
52
55
|
}
|
|
53
56
|
if let toolkit {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
if compression.toolbarHidden {
|
|
58
|
+
// an open tab rail takes this slot for a moment
|
|
59
|
+
EmptyView()
|
|
60
|
+
} else if compression.toolbarCompressed {
|
|
61
|
+
// HIG "Toolbar compressed": tools fold into one button, distinct from the useMore icon
|
|
62
|
+
SideBarButton(
|
|
63
|
+
icon: "navigation_menu",
|
|
64
|
+
headerColor: headerColor,
|
|
65
|
+
showBadge: toolkit.data?.showBadge ?? false,
|
|
66
|
+
accessibilityId: "btn_navigation_toolbar",
|
|
67
|
+
action: compression.expandToolbar
|
|
68
|
+
)
|
|
69
|
+
} else {
|
|
70
|
+
Toolkit(
|
|
71
|
+
headerRight: toolkit.data ?? HeaderRightData(),
|
|
72
|
+
tintColor: toolkit.tintColor,
|
|
73
|
+
vertical: true
|
|
74
|
+
)
|
|
75
|
+
.environment(\.headerColor, headerColor)
|
|
76
|
+
}
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
79
|
.padding(isRight ? .leading : .trailing, Spacing.M)
|
|
@@ -70,15 +88,17 @@ struct HeaderSideBar: View {
|
|
|
70
88
|
.padding(.top, top)
|
|
71
89
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: isRight ? .topTrailing : .topLeading)
|
|
72
90
|
.ignoresSafeArea()
|
|
91
|
+
.onAppear { compression.setToolbarHeight(fullHeight) }
|
|
92
|
+
.onChange(of: fullHeight) { compression.setToolbarHeight($0) }
|
|
73
93
|
}
|
|
74
94
|
}
|
|
75
95
|
|
|
76
96
|
/// Dark glass under light icons (banner headers), light glass otherwise.
|
|
77
97
|
private struct SideBarGlass<S: Shape>: ViewModifier {
|
|
78
|
-
let
|
|
98
|
+
let headerColor: HeaderColor
|
|
79
99
|
let shape: S
|
|
80
100
|
|
|
81
|
-
private var isDark: Bool {
|
|
101
|
+
private var isDark: Bool { headerColor.tintIconColor == Colors.black01 || headerColor.tintIconColor == .white }
|
|
82
102
|
|
|
83
103
|
func body(content: Content) -> some View {
|
|
84
104
|
if isDark {
|
|
@@ -99,31 +119,32 @@ private struct SideBarGlass<S: Shape>: ViewModifier {
|
|
|
99
119
|
/// 48pt glass circle for a single side bar control.
|
|
100
120
|
struct SideBarButton: View {
|
|
101
121
|
let icon: String
|
|
102
|
-
|
|
122
|
+
let headerColor: HeaderColor
|
|
103
123
|
var showBadge: Bool = false
|
|
104
124
|
var disabled: Bool = false
|
|
105
125
|
var accessibilityId: String?
|
|
106
126
|
let action: () -> Void
|
|
107
127
|
|
|
108
128
|
var body: some View {
|
|
109
|
-
SwiftUI.Button(action:
|
|
110
|
-
Icon(source: icon, size: sideBarIconSize, color:
|
|
129
|
+
SwiftUI.Button(action: action) {
|
|
130
|
+
Icon(source: icon, size: sideBarIconSize, color: headerColor.tintIconColor)
|
|
111
131
|
.frame(width: DuoMetrics.controlSize, height: DuoMetrics.controlSize)
|
|
112
|
-
.modifier(SideBarGlass(
|
|
132
|
+
.modifier(SideBarGlass(headerColor: headerColor, shape: Circle()))
|
|
113
133
|
.overlay(alignment: .topTrailing) {
|
|
114
134
|
if showBadge {
|
|
115
135
|
BadgeDot(size: .small).offset(x: -Spacing.XXS, y: Spacing.XXS)
|
|
116
136
|
}
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
|
-
.buttonStyle(
|
|
139
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
140
|
+
.disabled(disabled)
|
|
120
141
|
.accessibilityIdentifier(accessibilityId ?? "")
|
|
121
142
|
}
|
|
122
143
|
}
|
|
123
144
|
|
|
124
145
|
/// 48pt wide glass capsule grouping related controls, one item per 50pt.
|
|
125
146
|
struct SideBarGroup<Content: View>: View {
|
|
126
|
-
|
|
147
|
+
let headerColor: HeaderColor
|
|
127
148
|
let itemCount: Int
|
|
128
149
|
@ViewBuilder let content: () -> Content
|
|
129
150
|
|
|
@@ -133,23 +154,23 @@ struct SideBarGroup<Content: View>: View {
|
|
|
133
154
|
width: DuoMetrics.controlSize,
|
|
134
155
|
height: itemCount > 1 ? DuoMetrics.itemPitch * CGFloat(itemCount) : DuoMetrics.controlSize
|
|
135
156
|
)
|
|
136
|
-
.modifier(SideBarGlass(
|
|
157
|
+
.modifier(SideBarGlass(headerColor: headerColor, shape: RoundedRectangle(cornerRadius: Radius.XL)))
|
|
137
158
|
}
|
|
138
159
|
}
|
|
139
160
|
|
|
140
161
|
struct SideBarGroupItem: View {
|
|
141
162
|
let icon: String
|
|
142
|
-
|
|
163
|
+
let headerColor: HeaderColor
|
|
143
164
|
let accessibilityId: String
|
|
144
165
|
let action: () -> Void
|
|
145
166
|
|
|
146
167
|
var body: some View {
|
|
147
168
|
SwiftUI.Button(action: action) {
|
|
148
|
-
Icon(source: icon, size: sideBarIconSize, color:
|
|
169
|
+
Icon(source: icon, size: sideBarIconSize, color: headerColor.tintIconColor)
|
|
149
170
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
150
171
|
.contentShape(Rectangle())
|
|
151
172
|
}
|
|
152
|
-
.buttonStyle(
|
|
173
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
153
174
|
.accessibilityIdentifier(accessibilityId)
|
|
154
175
|
}
|
|
155
176
|
}
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// SnackBar.swift
|
|
3
3
|
// MoMoUIKits
|
|
4
4
|
//
|
|
5
|
-
// SwiftUI mirror of Compose's `
|
|
6
|
-
//
|
|
7
|
-
// indicator), stays for `duration`, then slides back down and is removed.
|
|
5
|
+
// SwiftUI mirror of Compose's `navigation/component/SnackBar.kt`: slides up above the screen's
|
|
6
|
+
// footer (or the home indicator), stays for `duration`, then slides back down.
|
|
8
7
|
// `Navigator.showSnackBar(_:)` drives it through the overplay host.
|
|
9
8
|
//
|
|
10
9
|
|
|
@@ -53,8 +52,8 @@ public struct SnackBar {
|
|
|
53
52
|
@available(iOS 16.0, *)
|
|
54
53
|
struct SnackBarView: View {
|
|
55
54
|
let snackBar: SnackBar
|
|
56
|
-
///
|
|
57
|
-
/// (
|
|
55
|
+
/// Distance from the window bottom to the top of the screen's footer, or the navigation bar
|
|
56
|
+
/// inset without one (Compose's `footerHeight` offset).
|
|
58
57
|
let bottomInset: CGFloat
|
|
59
58
|
|
|
60
59
|
@EnvironmentObject private var navigator: Navigator
|
|
@@ -78,6 +77,7 @@ struct SnackBarView: View {
|
|
|
78
77
|
.padding(.bottom, bottomInset)
|
|
79
78
|
}
|
|
80
79
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
80
|
+
.ignoresSafeArea()
|
|
81
81
|
.onAppear {
|
|
82
82
|
guard !didAppear else { return }
|
|
83
83
|
didAppear = true
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HostBackGestureBridge.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `navigation/HostBackGestureBridge.kt`: the host owns the edge-pan
|
|
6
|
+
// recogniser and routes a back into the topmost kit stack. NavigationContainer attaches itself.
|
|
7
|
+
// The kit stack is a NavigationStack, so there is no seekable transition: the host only pops.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import Foundation
|
|
11
|
+
|
|
12
|
+
@available(iOS 16.0, *)
|
|
13
|
+
public final class HostBackGestureBridge {
|
|
14
|
+
public static let shared = HostBackGestureBridge()
|
|
15
|
+
private init() {}
|
|
16
|
+
|
|
17
|
+
// Weak: a container torn down while its cover is up never reports its disappearance.
|
|
18
|
+
private var navigators: [WeakNavigator] = []
|
|
19
|
+
|
|
20
|
+
/// Set while a bottom tab other than the first is selected; the host shows its floating arrow.
|
|
21
|
+
public var shouldUseFloatingArrowBack = false
|
|
22
|
+
|
|
23
|
+
public func attach(_ navigator: Navigator) {
|
|
24
|
+
navigators.removeAll { $0.value == nil }
|
|
25
|
+
guard !navigators.contains(where: { $0.value === navigator }) else { return }
|
|
26
|
+
navigators.append(WeakNavigator(value: navigator))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public func detach(_ navigator: Navigator) {
|
|
30
|
+
navigators.removeAll { $0.value == nil || $0.value === navigator }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Whether a kit stack is on screen at all.
|
|
34
|
+
public func checkCanPop() -> Bool { top() != nil }
|
|
35
|
+
|
|
36
|
+
/// Whether the top stack pops a screen itself; false means a back dismisses the mini app.
|
|
37
|
+
public func canPopInner() -> Bool { top()?.canPop() == true }
|
|
38
|
+
|
|
39
|
+
public func pop() { top()?.onBackSafe() }
|
|
40
|
+
|
|
41
|
+
@available(*, deprecated, renamed: "attach")
|
|
42
|
+
public func push(_ navigator: Navigator) { attach(navigator) }
|
|
43
|
+
|
|
44
|
+
@available(*, deprecated, renamed: "detach")
|
|
45
|
+
public func remove(_ navigator: Navigator) { detach(navigator) }
|
|
46
|
+
|
|
47
|
+
private func top() -> Navigator? { navigators.last { $0.value != nil }?.value }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@available(iOS 16.0, *)
|
|
51
|
+
@available(*, deprecated, renamed: "HostBackGestureBridge")
|
|
52
|
+
public typealias NavigatorManager = HostBackGestureBridge
|
|
53
|
+
|
|
54
|
+
@available(iOS 16.0, *)
|
|
55
|
+
private struct WeakNavigator {
|
|
56
|
+
weak var value: Navigator?
|
|
57
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Navigation.swift
|
|
3
|
+
// MoMoUIKits
|
|
4
|
+
//
|
|
5
|
+
// SwiftUI mirror of Compose's `navigation/Navigation.kt`: a screen's `NavigationOptions` and the
|
|
6
|
+
// per-screen `Navigation` that updates them at runtime.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import SwiftUI
|
|
10
|
+
|
|
11
|
+
/// Per-screen handle (Compose `LocalNavigation`). Read it inside a screen with
|
|
12
|
+
/// `@EnvironmentObject var navigation: Navigation` and call `setOptions` to change the header,
|
|
13
|
+
/// footer or back handling after the screen is shown.
|
|
14
|
+
public final class Navigation: ObservableObject {
|
|
15
|
+
public let id: Int
|
|
16
|
+
public let bottomTabIndex: Int
|
|
17
|
+
@Published public private(set) var options: NavigationOptions
|
|
18
|
+
|
|
19
|
+
private weak var registry: DynamicScreenRegistry?
|
|
20
|
+
/// What the footer (or navigation bar) takes from the window bottom; lifts snack bars.
|
|
21
|
+
var bottomObstruction: CGFloat = 0
|
|
22
|
+
|
|
23
|
+
init(
|
|
24
|
+
id: Int = -1,
|
|
25
|
+
bottomTabIndex: Int = -1,
|
|
26
|
+
options: NavigationOptions?,
|
|
27
|
+
registry: DynamicScreenRegistry? = nil
|
|
28
|
+
) {
|
|
29
|
+
self.id = id
|
|
30
|
+
self.bottomTabIndex = bottomTabIndex
|
|
31
|
+
self.options = options ?? NavigationOptions()
|
|
32
|
+
self.registry = registry
|
|
33
|
+
if id != -1 { registry?.bind(self) }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public func setOptions(_ options: NavigationOptions) {
|
|
37
|
+
update(options)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Changes only what the closure touches, like Compose's named-argument `setOptions`.
|
|
41
|
+
public func setOptions(_ transform: (inout NavigationOptions) -> Void) {
|
|
42
|
+
var updated = options
|
|
43
|
+
transform(&updated)
|
|
44
|
+
update(updated)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private func update(_ updated: NavigationOptions) {
|
|
48
|
+
options = updated
|
|
49
|
+
// back handling reads the registry copy
|
|
50
|
+
if id != -1 { registry?.setOptions(id: id, options: updated) }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public struct NavigationOptions {
|
|
55
|
+
public var screenName: String?
|
|
56
|
+
public var title: String
|
|
57
|
+
/// Rich header title (Compose `headerTitle`); `title` is used when nil.
|
|
58
|
+
public var headerTitle: HeaderTitle?
|
|
59
|
+
public var titlePosition: TitlePosition
|
|
60
|
+
public var headerType: HeaderType
|
|
61
|
+
/// Compose's `HeaderType.Default/Extended(useAnimated:)`: with `inputSearchProps`, the search
|
|
62
|
+
/// rises into the header on scroll.
|
|
63
|
+
public var useAnimated: Bool
|
|
64
|
+
/// Compose's `HeaderType.Transparent`; with `fullScreenContent` the content starts under it.
|
|
65
|
+
public var headerTransparent: Bool
|
|
66
|
+
public var fullScreenContent: Bool
|
|
67
|
+
public var hiddenBack: Bool
|
|
68
|
+
public var backgroundColor: Color?
|
|
69
|
+
public var tintColor: Color?
|
|
70
|
+
public var scrollable: Bool
|
|
71
|
+
/// Compose's `ScrollData.scrollToTopCallback`, fired on a status-bar tap.
|
|
72
|
+
public var scrollToTopCallback: (() -> Void)?
|
|
73
|
+
public var useAvoidKeyboard: Bool
|
|
74
|
+
/// Compose's `KeyboardOptions.keyboardShouldPersistTaps`: a tap anywhere hides the keyboard.
|
|
75
|
+
public var keyboardShouldPersistTaps: Bool
|
|
76
|
+
public var headerBackProps: HeaderBackProps?
|
|
77
|
+
/// Toolkit data for the default header right (Compose `HeaderRight.Toolkit`). Use `headerRight`
|
|
78
|
+
/// for a custom view and nil for none.
|
|
79
|
+
public var headerRightData: HeaderRightData?
|
|
80
|
+
public var headerRight: (() -> any View)?
|
|
81
|
+
public var footer: (() -> AnyView)?
|
|
82
|
+
public var footerTransparent: Bool
|
|
83
|
+
public var footerUseMargin: Bool
|
|
84
|
+
public var animatedHeader: AnimatedHeader?
|
|
85
|
+
public var layoutOffset: CGFloat
|
|
86
|
+
public var inputSearchProps: InputSearchProps?
|
|
87
|
+
public var fabProps: FabProps?
|
|
88
|
+
public var onBackHandler: (() -> Void)?
|
|
89
|
+
|
|
90
|
+
public init(
|
|
91
|
+
screenName: String? = nil,
|
|
92
|
+
title: String = "Stack",
|
|
93
|
+
headerTitle: HeaderTitle? = nil,
|
|
94
|
+
titlePosition: TitlePosition = .left,
|
|
95
|
+
headerType: HeaderType = .default,
|
|
96
|
+
useAnimated: Bool = false,
|
|
97
|
+
headerTransparent: Bool = false,
|
|
98
|
+
fullScreenContent: Bool = false,
|
|
99
|
+
hiddenBack: Bool = false,
|
|
100
|
+
backgroundColor: Color? = nil,
|
|
101
|
+
tintColor: Color? = Colors.black17,
|
|
102
|
+
scrollable: Bool = true,
|
|
103
|
+
scrollToTopCallback: (() -> Void)? = nil,
|
|
104
|
+
useAvoidKeyboard: Bool = true,
|
|
105
|
+
keyboardShouldPersistTaps: Bool = false,
|
|
106
|
+
headerBackProps: HeaderBackProps? = nil,
|
|
107
|
+
headerRightData: HeaderRightData? = nil,
|
|
108
|
+
headerRight: (() -> any View)? = { HeaderRight() },
|
|
109
|
+
footer: (() -> AnyView)? = nil,
|
|
110
|
+
footerTransparent: Bool = false,
|
|
111
|
+
footerUseMargin: Bool = true,
|
|
112
|
+
animatedHeader: AnimatedHeader? = nil,
|
|
113
|
+
layoutOffset: CGFloat = 56,
|
|
114
|
+
inputSearchProps: InputSearchProps? = nil,
|
|
115
|
+
fabProps: FabProps? = nil,
|
|
116
|
+
onBackHandler: (() -> Void)? = nil
|
|
117
|
+
) {
|
|
118
|
+
self.screenName = screenName
|
|
119
|
+
self.title = title
|
|
120
|
+
self.headerTitle = headerTitle
|
|
121
|
+
self.titlePosition = titlePosition
|
|
122
|
+
self.headerType = headerType
|
|
123
|
+
self.useAnimated = useAnimated
|
|
124
|
+
self.headerTransparent = headerTransparent
|
|
125
|
+
self.fullScreenContent = fullScreenContent
|
|
126
|
+
self.hiddenBack = hiddenBack
|
|
127
|
+
self.backgroundColor = backgroundColor
|
|
128
|
+
self.tintColor = tintColor
|
|
129
|
+
self.scrollable = scrollable
|
|
130
|
+
self.scrollToTopCallback = scrollToTopCallback
|
|
131
|
+
self.useAvoidKeyboard = useAvoidKeyboard
|
|
132
|
+
self.keyboardShouldPersistTaps = keyboardShouldPersistTaps
|
|
133
|
+
self.headerBackProps = headerBackProps
|
|
134
|
+
self.headerRightData = headerRightData
|
|
135
|
+
self.headerRight = headerRight
|
|
136
|
+
self.footer = footer
|
|
137
|
+
self.footerTransparent = footerTransparent
|
|
138
|
+
self.footerUseMargin = footerUseMargin
|
|
139
|
+
self.animatedHeader = animatedHeader
|
|
140
|
+
self.layoutOffset = layoutOffset
|
|
141
|
+
self.inputSearchProps = inputSearchProps
|
|
142
|
+
self.fabProps = fabProps
|
|
143
|
+
self.onBackHandler = onBackHandler
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
extension NavigationOptions {
|
|
148
|
+
var resolvedHeaderRight: (() -> any View)? {
|
|
149
|
+
guard let headerRightData else { return headerRight }
|
|
150
|
+
return { HeaderRight(headerRight: headerRightData, tintColor: tintColor) }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// `StackScreen` built from options, shared by stack screens and bottom-tab screens.
|
|
155
|
+
struct OptionsStackScreen: View {
|
|
156
|
+
let options: NavigationOptions
|
|
157
|
+
let screenName: String
|
|
158
|
+
let goBack: (() -> Void)?
|
|
159
|
+
let content: () -> AnyView
|
|
160
|
+
|
|
161
|
+
var body: some View {
|
|
162
|
+
StackScreen(
|
|
163
|
+
backgroundColor: options.backgroundColor,
|
|
164
|
+
headerTransparent: options.headerTransparent,
|
|
165
|
+
fullScreenContent: options.fullScreenContent,
|
|
166
|
+
tintColor: options.tintColor ?? Colors.black17,
|
|
167
|
+
isBack: !options.hiddenBack,
|
|
168
|
+
headerType: options.headerType,
|
|
169
|
+
useAnimated: options.useAnimated,
|
|
170
|
+
title: options.title,
|
|
171
|
+
headerTitle: options.headerTitle,
|
|
172
|
+
titlePosition: options.titlePosition,
|
|
173
|
+
goBack: options.hiddenBack ? nil : goBack,
|
|
174
|
+
scrollable: options.scrollable,
|
|
175
|
+
scrollToTopCallback: options.scrollToTopCallback,
|
|
176
|
+
useAvoidKeyboard: options.useAvoidKeyboard,
|
|
177
|
+
keyboardShouldPersistTaps: options.keyboardShouldPersistTaps,
|
|
178
|
+
footer: options.footer,
|
|
179
|
+
footerTransparent: options.footerTransparent,
|
|
180
|
+
footerUseMargin: options.footerUseMargin,
|
|
181
|
+
headerRight: options.resolvedHeaderRight,
|
|
182
|
+
animatedHeader: options.animatedHeader,
|
|
183
|
+
layoutOffset: options.layoutOffset,
|
|
184
|
+
inputSearchProps: options.inputSearchProps,
|
|
185
|
+
fabProps: options.fabProps,
|
|
186
|
+
screenName: screenName,
|
|
187
|
+
content: content
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// NavigationContainer.swift
|
|
3
3
|
// MoMoUIKits
|
|
4
4
|
//
|
|
5
|
-
// SwiftUI mirror of Compose's NavigationContainer.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// SwiftUI mirror of Compose's `navigation/NavigationContainer.kt`: wraps NavigationStack
|
|
6
|
+
// (iOS 16+), injects Navigator, theme, localize and font scale, and renders every registered
|
|
7
|
+
// screen inside `StackScreen` chrome built from its options.
|
|
8
8
|
//
|
|
9
9
|
|
|
10
10
|
import SwiftUI
|
|
@@ -15,14 +15,19 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
15
15
|
@StateObject private var applicationEnvironment: ApplicationEnvironment
|
|
16
16
|
@StateObject private var localize: Localize
|
|
17
17
|
|
|
18
|
+
@Environment(\.appTheme) private var parentTheme
|
|
19
|
+
|
|
18
20
|
private let setNavigator: ((Navigator) -> Void)?
|
|
19
21
|
private let composeApi: KitComposeApi?
|
|
20
22
|
private let maxApi: MaxApi?
|
|
23
|
+
private let initialTheme: Theme?
|
|
21
24
|
|
|
25
|
+
/// `initialTheme` nil keeps the theme the host already put in the environment.
|
|
22
26
|
public init(
|
|
23
27
|
initialScreenName: String = "",
|
|
24
28
|
@ViewBuilder initialScreen: @escaping () -> Initial,
|
|
25
29
|
options: NavigationOptions? = nil,
|
|
30
|
+
initialTheme: Theme? = nil,
|
|
26
31
|
applicationContext: MiniAppContext? = nil,
|
|
27
32
|
composeApi: KitComposeApi? = nil,
|
|
28
33
|
config: KitConfig? = nil,
|
|
@@ -36,6 +41,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
36
41
|
options: options,
|
|
37
42
|
composeApi: composeApi
|
|
38
43
|
)
|
|
44
|
+
nav.maxApi = maxApi
|
|
39
45
|
_navigator = StateObject(wrappedValue: nav)
|
|
40
46
|
_applicationEnvironment = StateObject(
|
|
41
47
|
wrappedValue: ApplicationEnvironment(
|
|
@@ -50,9 +56,20 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
50
56
|
// AI-GENERATED END: default to the app-language-observing shared Localize
|
|
51
57
|
self.composeApi = composeApi
|
|
52
58
|
self.maxApi = maxApi
|
|
59
|
+
self.initialTheme = initialTheme
|
|
53
60
|
self.setNavigator = setNavigator
|
|
54
61
|
}
|
|
55
62
|
|
|
63
|
+
/// RN parity: the theme's own header image wins; config `headerBar` is only the fallback.
|
|
64
|
+
private var resolvedTheme: Theme {
|
|
65
|
+
let theme = initialTheme ?? parentTheme
|
|
66
|
+
guard !theme.assets.isHeaderImage,
|
|
67
|
+
let headerBar = applicationEnvironment.config?.headerBar, !headerBar.isEmpty else {
|
|
68
|
+
return theme
|
|
69
|
+
}
|
|
70
|
+
return theme.copy(assets: ThemeAssets(headerBackground: headerBar))
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
public var body: some View {
|
|
57
74
|
withKitEnvironment(
|
|
58
75
|
stack(path: $navigator.path, root: navigator.rootRoute)
|
|
@@ -76,10 +93,12 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
76
93
|
if navigator.composeApi == nil {
|
|
77
94
|
navigator.composeApi = composeApi
|
|
78
95
|
}
|
|
96
|
+
if navigator.maxApi == nil {
|
|
97
|
+
navigator.maxApi = maxApi
|
|
98
|
+
}
|
|
79
99
|
setNavigator?(navigator)
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
NavigatorManager.shared.push(navigator)
|
|
100
|
+
// Idempotent, so a re-appear won't double-count.
|
|
101
|
+
HostBackGestureBridge.shared.attach(navigator)
|
|
83
102
|
// AI-GENERATED START: demote the MaxApi language pull to a seed of last resort
|
|
84
103
|
// Only a seed: a Localize with a language source already tracks the host in both
|
|
85
104
|
// directions, so asking maxApi would just race it. `changeLanguage` normalizes the
|
|
@@ -93,7 +112,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
93
112
|
.onDisappear {
|
|
94
113
|
// Our own cover hides the stack without leaving it; host back must still reach it.
|
|
95
114
|
guard navigator.presented == nil else { return }
|
|
96
|
-
|
|
115
|
+
HostBackGestureBridge.shared.detach(navigator)
|
|
97
116
|
}
|
|
98
117
|
}
|
|
99
118
|
|
|
@@ -132,6 +151,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
132
151
|
.environmentObject(navigator)
|
|
133
152
|
.environmentObject(applicationEnvironment)
|
|
134
153
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
154
|
+
.environment(\.appTheme, resolvedTheme)
|
|
135
155
|
.momoLocalize(localize)
|
|
136
156
|
.momoFontScale(context: applicationEnvironment.applicationContext)
|
|
137
157
|
}
|
|
@@ -143,34 +163,24 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
143
163
|
private struct ScreenHost: View {
|
|
144
164
|
let screen: DynamicScreen
|
|
145
165
|
let navigator: Navigator
|
|
166
|
+
@StateObject private var navigation: Navigation
|
|
167
|
+
|
|
168
|
+
init(screen: DynamicScreen, navigator: Navigator) {
|
|
169
|
+
self.screen = screen
|
|
170
|
+
self.navigator = navigator
|
|
171
|
+
_navigation = StateObject(
|
|
172
|
+
wrappedValue: Navigation(id: screen.id, options: screen.options, registry: navigator.registry)
|
|
173
|
+
)
|
|
174
|
+
}
|
|
146
175
|
|
|
147
176
|
var body: some View {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
backgroundColor: options.backgroundColor,
|
|
151
|
-
headerTransparent: options.headerTransparent,
|
|
152
|
-
fullScreenContent: options.fullScreenContent,
|
|
153
|
-
tintColor: options.tintColor ?? Colors.black17,
|
|
154
|
-
isBack: !options.hiddenBack,
|
|
155
|
-
headerType: options.headerType,
|
|
156
|
-
useAnimated: options.useAnimated,
|
|
157
|
-
title: options.title,
|
|
158
|
-
headerTitle: options.headerTitle,
|
|
159
|
-
titlePosition: options.titlePosition,
|
|
160
|
-
goBack: options.hiddenBack ? nil : { navigator.onBackSafe() },
|
|
161
|
-
scrollable: options.scrollable,
|
|
162
|
-
scrollToTopCallback: options.scrollToTopCallback,
|
|
163
|
-
useAvoidKeyboard: options.useAvoidKeyboard,
|
|
164
|
-
keyboardShouldPersistTaps: options.keyboardShouldPersistTaps,
|
|
165
|
-
footer: options.footer,
|
|
166
|
-
headerRight: options.resolvedHeaderRight,
|
|
167
|
-
animatedHeader: options.animatedHeader,
|
|
168
|
-
layoutOffset: options.layoutOffset,
|
|
169
|
-
inputSearchProps: options.inputSearchProps,
|
|
170
|
-
fabProps: options.fabProps,
|
|
177
|
+
OptionsStackScreen(
|
|
178
|
+
options: navigation.options,
|
|
171
179
|
screenName: screen.name,
|
|
172
|
-
|
|
180
|
+
goBack: { navigator.onBackSafe() },
|
|
181
|
+
content: screen.content
|
|
173
182
|
)
|
|
183
|
+
.environmentObject(navigation)
|
|
174
184
|
// Per screen, not around the NavigationStack: a state-driven reader there stops path pushes rendering.
|
|
175
185
|
.momoScreenLayout()
|
|
176
186
|
}
|
|
@@ -196,24 +206,19 @@ private struct OverplayHost: View {
|
|
|
196
206
|
BottomSheetView(
|
|
197
207
|
header: item.header ?? .title(),
|
|
198
208
|
isSurface: item.isSurface,
|
|
199
|
-
barrierDismissible: item.barrierDismissible
|
|
209
|
+
barrierDismissible: item.barrierDismissible,
|
|
210
|
+
draggable: item.draggable,
|
|
211
|
+
useBottomInset: item.useBottomInset,
|
|
212
|
+
footer: item.footer
|
|
200
213
|
) {
|
|
201
214
|
item.content()
|
|
202
215
|
}
|
|
203
216
|
.id(item.id)
|
|
204
217
|
case .snackBar:
|
|
205
218
|
if let snackBar = item.snackBar {
|
|
206
|
-
SnackBarView(snackBar: snackBar, bottomInset:
|
|
219
|
+
SnackBarView(snackBar: snackBar, bottomInset: navigator.snackBarBottomInset())
|
|
207
220
|
.id(item.id)
|
|
208
221
|
}
|
|
209
222
|
}
|
|
210
223
|
}
|
|
211
224
|
}
|
|
212
|
-
|
|
213
|
-
/// Bottom safe-area inset (home indicator), used to lift the snack bar.
|
|
214
|
-
@available(iOS 16.0, *)
|
|
215
|
-
private func safeAreaBottomInset() -> CGFloat {
|
|
216
|
-
UIApplication.shared.connectedScenes
|
|
217
|
-
.compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
|
|
218
|
-
.first ?? 0
|
|
219
|
-
}
|