@momo-kits/native-kits 0.167.1-duo.1-debug → 0.167.1-duo.10-debug

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CLAUDE.md +32 -0
  2. package/compose/build.gradle.kts +15 -1
  3. package/compose/build.gradle.kts.backup +14 -0
  4. package/compose/compose.podspec +1 -1
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +1 -1
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +1 -1
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +53 -26
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -2
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +13 -6
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +5 -2
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +21 -9
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +232 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +4 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +143 -31
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +100 -66
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +22 -16
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +66 -18
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +23 -14
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +81 -24
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +4 -1
  56. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +4 -0
  57. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +86 -0
  58. package/gradle.properties +1 -1
  59. package/ios/Application/ApplicationEnvironment.swift +17 -0
  60. package/ios/Application/ComposeApi.swift +2 -1
  61. package/ios/Application/{Components.swift → LegacyHeader.swift} +7 -65
  62. package/ios/Application/Screen.swift +3 -2
  63. package/ios/Avatar/Avatar.swift +4 -3
  64. package/ios/Badge/Badge.swift +6 -4
  65. package/ios/Badge/BadgeDot.swift +1 -1
  66. package/ios/Badge/BadgeRibbon.swift +0 -1
  67. package/ios/Button/Button.swift +15 -12
  68. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
  69. package/ios/Carousel/Carousel.swift +29 -12
  70. package/ios/Checkbox/Checkbox.swift +7 -3
  71. package/ios/Chip/Chip.swift +5 -4
  72. package/ios/Collapse/Collapse.swift +70 -19
  73. package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
  74. package/ios/DateTimePicker/DateTimePicker.swift +54 -20
  75. package/ios/DateTimePicker/WheelPicker.swift +103 -105
  76. package/ios/Divider/Divider.swift +37 -4
  77. package/ios/Extensions/Color++.swift +38 -9
  78. package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
  79. package/ios/Extensions/SkeletonUICompat.swift +11 -0
  80. package/ios/Icon/Icon.swift +21 -23
  81. package/ios/Icon/Icons.swift +1329 -0
  82. package/ios/IconButton/IconButton.swift +15 -7
  83. package/ios/Image/Image.swift +7 -23
  84. package/ios/Information/Information.swift +27 -9
  85. package/ios/Input/ErrorView.swift +2 -2
  86. package/ios/Input/Input.swift +160 -147
  87. package/ios/Input/InputPhoneNumber.swift +12 -9
  88. package/ios/Input/InputSearch.swift +102 -75
  89. package/ios/Input/InputTextArea.swift +128 -145
  90. package/ios/InputDropDown/InputDropDown.swift +10 -7
  91. package/ios/InputMoney/InputMoney.swift +33 -11
  92. package/ios/InputOTP/InputOTP.swift +22 -12
  93. package/ios/Layout/Card.swift +50 -0
  94. package/ios/Layout/GridContext.swift +123 -3
  95. package/ios/Layout/Item.swift +5 -4
  96. package/ios/Layout/ScreenLayout.swift +312 -0
  97. package/ios/Layout/Section.swift +4 -100
  98. package/ios/Loader/Loader.swift +5 -9
  99. package/ios/Lottie/LottieView.swift +14 -2
  100. package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +97 -8
  101. package/ios/Navigation/BottomTab/BottomTab.swift +187 -0
  102. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +15 -7
  103. package/ios/Navigation/BottomTab/DuoBarCompression.swift +105 -0
  104. package/ios/Navigation/BottomTab/DuoTabBar.swift +304 -0
  105. package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
  106. package/ios/Navigation/Component/Header.swift +493 -0
  107. package/ios/Navigation/Component/HeaderBackground.swift +134 -0
  108. package/ios/Navigation/Component/HeaderJourney.swift +73 -0
  109. package/ios/Navigation/Component/HeaderLocation.swift +48 -0
  110. package/ios/Navigation/Component/HeaderRight.swift +386 -0
  111. package/ios/Navigation/Component/HeaderTitle.swift +36 -0
  112. package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
  113. package/ios/Navigation/Component/SideBar.swift +176 -0
  114. package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
  115. package/ios/Navigation/HostBackGestureBridge.swift +57 -0
  116. package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +5 -1
  117. package/ios/Navigation/Navigation.swift +190 -0
  118. package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +92 -81
  119. package/ios/{Application/Navigation → Navigation}/Navigator.swift +185 -36
  120. package/ios/Navigation/StackScreen.swift +782 -0
  121. package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
  122. package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
  123. package/ios/Pagination/PaginationScroll.swift +2 -2
  124. package/ios/Popup/PopupDisplay.swift +72 -136
  125. package/ios/Popup/PopupInput.swift +10 -13
  126. package/ios/Popup/PopupLayout.swift +140 -0
  127. package/ios/Popup/PopupNotify.swift +56 -73
  128. package/ios/Popup/PopupPromotion.swift +31 -47
  129. package/ios/PopupView/FullscreenPopup.swift +13 -19
  130. package/ios/PopupView/PopupView.swift +1 -1
  131. package/ios/PopupView/Utils++.swift +0 -8
  132. package/ios/ProgressInfo/ProgressInfo.swift +4 -2
  133. package/ios/Radio/Radio.swift +2 -1
  134. package/ios/Rating/Rating.swift +9 -1
  135. package/ios/Resources/dot_loading.json +558 -0
  136. package/ios/Resources/lottie_circle_loader.json +1 -0
  137. package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
  138. package/ios/Skeleton/Skeleton.swift +22 -27
  139. package/ios/Slider/Slider.swift +16 -18
  140. package/ios/Stepper/Stepper.swift +6 -1
  141. package/ios/Steps/Steps.swift +8 -6
  142. package/ios/SuggestAction/SuggestAction.swift +5 -0
  143. package/ios/Swipeable/SwipeCell.swift +10 -6
  144. package/ios/Switch/Switch.swift +22 -20
  145. package/ios/TabView/TabView.swift +102 -21
  146. package/ios/Tag/Tag.swift +15 -4
  147. package/ios/Template/Logo/Logo.swift +2 -1
  148. package/ios/Template/TrustBanner/TrustBanner.swift +106 -54
  149. package/ios/Title/Title.swift +6 -17
  150. package/ios/Tooltip/Tooltip.swift +29 -14
  151. package/ios/Typography/Text.swift +61 -19
  152. package/ios/Uploader/Uploader.swift +3 -1
  153. package/ios/native-kits.podspec +2 -1
  154. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  155. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
  156. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
  157. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +1 -0
  158. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
  159. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
  160. package/package.json +1 -1
  161. package/scripts/gen-ios-icons.mjs +36 -0
  162. package/.claude/settings.local.json +0 -185
  163. package/ios/Application/HeaderRight.swift +0 -306
  164. package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
  165. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  166. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  167. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  168. package/ios/Application/Navigation/NavigatorManager.swift +0 -22
  169. package/ios/Application/Navigation/component/Header.swift +0 -178
  170. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
  171. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  172. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
  173. package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
  174. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
  175. package/ios/Application/StackScreen.swift +0 -492
  176. package/local.properties +0 -8
  177. /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
@@ -2,10 +2,9 @@
2
2
  // NavigationContainer.swift
3
3
  // MoMoUIKits
4
4
  //
5
- // SwiftUI mirror of Compose's NavigationContainer.
6
- // Wraps NavigationStack, injects Navigator into the environment, and renders
7
- // every registered screen inside `Screen(options)` chrome — same auto-wrap
8
- // behavior as Compose's StackScreen.
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.
9
8
  //
10
9
 
11
10
  import SwiftUI
@@ -16,14 +15,19 @@ public struct NavigationContainer<Initial: View>: View {
16
15
  @StateObject private var applicationEnvironment: ApplicationEnvironment
17
16
  @StateObject private var localize: Localize
18
17
 
18
+ @Environment(\.appTheme) private var parentTheme
19
+
19
20
  private let setNavigator: ((Navigator) -> Void)?
20
21
  private let composeApi: KitComposeApi?
21
22
  private let maxApi: MaxApi?
23
+ private let initialTheme: Theme?
22
24
 
25
+ /// `initialTheme` nil keeps the theme the host already put in the environment.
23
26
  public init(
24
27
  initialScreenName: String = "",
25
28
  @ViewBuilder initialScreen: @escaping () -> Initial,
26
29
  options: NavigationOptions? = nil,
30
+ initialTheme: Theme? = nil,
27
31
  applicationContext: MiniAppContext? = nil,
28
32
  composeApi: KitComposeApi? = nil,
29
33
  config: KitConfig? = nil,
@@ -37,6 +41,7 @@ public struct NavigationContainer<Initial: View>: View {
37
41
  options: options,
38
42
  composeApi: composeApi
39
43
  )
44
+ nav.maxApi = maxApi
40
45
  _navigator = StateObject(wrappedValue: nav)
41
46
  _applicationEnvironment = StateObject(
42
47
  wrappedValue: ApplicationEnvironment(
@@ -51,53 +56,49 @@ public struct NavigationContainer<Initial: View>: View {
51
56
  // AI-GENERATED END: default to the app-language-observing shared Localize
52
57
  self.composeApi = composeApi
53
58
  self.maxApi = maxApi
59
+ self.initialTheme = initialTheme
54
60
  self.setNavigator = setNavigator
55
61
  }
56
62
 
57
- public var body: some View {
58
- NavigationStack(path: $navigator.path) {
59
- screenView(for: navigator.rootRoute)
60
- .navigationDestination(for: DynamicScreenRoute.self) { route in
61
- screenView(for: route)
62
- }
63
- }
64
- .environmentObject(navigator)
65
- .environmentObject(applicationEnvironment)
66
- .environment(\.applicationEnvironment, applicationEnvironment)
67
- // AI-GENERATED START: route Localize + font-scale injection through the observing modifiers
68
- .momoLocalize(localize)
69
- .momoFontScale(context: applicationEnvironment.applicationContext)
70
- // AI-GENERATED END: route Localize + font-scale injection through the observing modifiers
71
- .fullScreenCover(item: $navigator.presented) { route in
72
- screenView(forDialog: route)
73
- .environmentObject(navigator)
74
- .environmentObject(applicationEnvironment)
75
- .environment(\.applicationEnvironment, applicationEnvironment)
76
- // AI-GENERATED START: route Localize + font-scale injection through the observing modifiers
77
- .momoLocalize(localize)
78
- .momoFontScale(context: applicationEnvironment.applicationContext)
79
- // AI-GENERATED END: route Localize + font-scale injection through the observing modifiers
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
80
69
  }
81
- .overlay {
82
- if let item = navigator.overplay {
83
- OverplayHost(item: item)
84
- .environmentObject(navigator)
85
- .environmentObject(applicationEnvironment)
86
- .environment(\.applicationEnvironment, applicationEnvironment)
87
- // AI-GENERATED START: route Localize + font-scale injection through the observing modifiers
88
- .momoLocalize(localize)
89
- .momoFontScale(context: applicationEnvironment.applicationContext)
90
- // AI-GENERATED END: route Localize + font-scale injection through the observing modifiers
91
- }
70
+ return theme.copy(assets: ThemeAssets(headerBackground: headerBar))
71
+ }
72
+
73
+ public var body: some View {
74
+ withKitEnvironment(
75
+ stack(path: $navigator.path, root: navigator.rootRoute)
76
+ .overlay { overplayLayer(presentedId: nil) }
77
+ )
78
+ .fullScreenCover(item: Binding(
79
+ get: { navigator.coverRoute },
80
+ // A close that bypasses the navigator (e.g. `dismiss()`) still tears the flow down.
81
+ // SwiftUI also writes nil after a navigator-driven close; by then a new screen may
82
+ // already be queued, so only a cover that is still showing counts.
83
+ set: { if $0 == nil, navigator.coverRoute != nil { navigator.dismiss() } }
84
+ )) { route in
85
+ withKitEnvironment(
86
+ stack(path: $navigator.presentedPath, root: DynamicScreenRoute(id: route.id))
87
+ .overlay { overplayLayer(presentedId: route.id) }
88
+ )
89
+ .onAppear { navigator.coverDidAppear() }
90
+ .onDisappear { navigator.coverDidDisappear() }
92
91
  }
93
92
  .onAppear {
94
93
  if navigator.composeApi == nil {
95
94
  navigator.composeApi = composeApi
96
95
  }
96
+ if navigator.maxApi == nil {
97
+ navigator.maxApi = maxApi
98
+ }
97
99
  setNavigator?(navigator)
98
- // Register with the host swipe-back bridge (mirrors ComposeAppContainer's
99
- // ComposeNavigatorManager.push). Idempotent, so a re-appear won't double-count.
100
- NavigatorManager.shared.push(navigator)
100
+ // Idempotent, so a re-appear won't double-count.
101
+ HostBackGestureBridge.shared.attach(navigator)
101
102
  // AI-GENERATED START: demote the MaxApi language pull to a seed of last resort
102
103
  // Only a seed: a Localize with a language source already tracks the host in both
103
104
  // directions, so asking maxApi would just race it. `changeLanguage` normalizes the
@@ -109,7 +110,20 @@ public struct NavigationContainer<Initial: View>: View {
109
110
  // AI-GENERATED END: demote the MaxApi language pull to a seed of last resort
110
111
  }
111
112
  .onDisappear {
112
- NavigatorManager.shared.remove(navigator)
113
+ // Our own cover hides the stack without leaving it; host back must still reach it.
114
+ guard navigator.presented == nil else { return }
115
+ HostBackGestureBridge.shared.detach(navigator)
116
+ }
117
+ }
118
+
119
+ private func stack(path: Binding<[DynamicScreenRoute]>, root: DynamicScreenRoute) -> some View {
120
+ NavigationStack(path: path) {
121
+ screenView(for: root)
122
+ // A reset swaps the root in place; without the id it would inherit the old root's state.
123
+ .id(root.id)
124
+ .navigationDestination(for: DynamicScreenRoute.self) { route in
125
+ screenView(for: route)
126
+ }
113
127
  }
114
128
  }
115
129
 
@@ -123,14 +137,24 @@ public struct NavigationContainer<Initial: View>: View {
123
137
  }
124
138
  }
125
139
 
140
+ /// Each context draws only the overplays shown over it, so one never re-mounts elsewhere.
126
141
  @ViewBuilder
127
- private func screenView(forDialog route: DynamicDialogRoute) -> some View {
128
- if let screen = navigator.registry.getScreen(id: route.id) {
129
- ScreenHost(screen: screen, navigator: navigator)
130
- } else {
131
- EmptyView()
142
+ private func overplayLayer(presentedId: Int?) -> some View {
143
+ if let item = navigator.overplay, item.presentedId == presentedId {
144
+ OverplayHost(item: item)
145
+ .momoScreenLayout()
132
146
  }
133
147
  }
148
+
149
+ private func withKitEnvironment<Content: View>(_ content: Content) -> some View {
150
+ content
151
+ .environmentObject(navigator)
152
+ .environmentObject(applicationEnvironment)
153
+ .environment(\.applicationEnvironment, applicationEnvironment)
154
+ .environment(\.appTheme, resolvedTheme)
155
+ .momoLocalize(localize)
156
+ .momoFontScale(context: applicationEnvironment.applicationContext)
157
+ }
134
158
  }
135
159
 
136
160
  // MARK: - ScreenHost
@@ -139,34 +163,26 @@ public struct NavigationContainer<Initial: View>: View {
139
163
  private struct ScreenHost: View {
140
164
  let screen: DynamicScreen
141
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
+ }
142
175
 
143
176
  var body: some View {
144
- let options = screen.options ?? NavigationOptions()
145
- StackScreen(
146
- backgroundColor: options.backgroundColor,
147
- headerTransparent: options.headerTransparent,
148
- fullScreenContent: options.fullScreenContent,
149
- tintColor: options.tintColor ?? Colors.black17,
150
- isBack: !options.hiddenBack,
151
- headerType: options.headerType,
152
- useAnimated: options.useAnimated,
153
- title: options.title,
154
- headerTitle: options.headerTitle,
155
- titlePosition: options.titlePosition,
156
- goBack: options.hiddenBack ? nil : { navigator.onBackSafe() },
157
- scrollable: options.scrollable,
158
- scrollToTopCallback: options.scrollToTopCallback,
159
- useAvoidKeyboard: options.useAvoidKeyboard,
160
- keyboardShouldPersistTaps: options.keyboardShouldPersistTaps,
161
- footer: options.footer,
162
- headerRight: options.resolvedHeaderRight,
163
- animatedHeader: options.animatedHeader,
164
- layoutOffset: options.layoutOffset,
165
- inputSearchProps: options.inputSearchProps,
166
- fabProps: options.fabProps,
177
+ OptionsStackScreen(
178
+ options: navigation.options,
167
179
  screenName: screen.name,
168
- content: { screen.content() }
180
+ goBack: { navigator.onBackSafe() },
181
+ content: screen.content
169
182
  )
183
+ .environmentObject(navigation)
184
+ // Per screen, not around the NavigationStack: a state-driven reader there stops path pushes rendering.
185
+ .momoScreenLayout()
170
186
  }
171
187
  }
172
188
 
@@ -190,24 +206,19 @@ private struct OverplayHost: View {
190
206
  BottomSheetView(
191
207
  header: item.header ?? .title(),
192
208
  isSurface: item.isSurface,
193
- barrierDismissible: item.barrierDismissible
209
+ barrierDismissible: item.barrierDismissible,
210
+ draggable: item.draggable,
211
+ useBottomInset: item.useBottomInset,
212
+ footer: item.footer
194
213
  ) {
195
214
  item.content()
196
215
  }
197
216
  .id(item.id)
198
217
  case .snackBar:
199
218
  if let snackBar = item.snackBar {
200
- SnackBarView(snackBar: snackBar, bottomInset: safeAreaBottomInset() + Spacing.S)
219
+ SnackBarView(snackBar: snackBar, bottomInset: navigator.snackBarBottomInset())
201
220
  .id(item.id)
202
221
  }
203
222
  }
204
223
  }
205
224
  }
206
-
207
- /// Bottom safe-area inset (home indicator), used to lift the snack bar.
208
- @available(iOS 16.0, *)
209
- private func safeAreaBottomInset() -> CGFloat {
210
- UIApplication.shared.connectedScenes
211
- .compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
212
- .first ?? 0
213
- }