@momo-kits/native-kits 0.167.1-duo.8 → 0.167.1-duo.9-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 (254) hide show
  1. package/CLAUDE.md +4 -1
  2. package/build.gradle.kts +11 -0
  3. package/compose/build.gradle.kts +204 -0
  4. package/compose/build.gradle.kts.backup +204 -0
  5. package/compose/compose.podspec +47 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  10. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +153 -0
  11. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +124 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +831 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +81 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +115 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +478 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +265 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +265 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +361 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +108 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +308 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +330 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +207 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +92 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +560 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +501 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +236 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +180 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +16 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +18 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +297 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +54 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +50 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +232 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +41 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +338 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +137 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +106 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +205 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +393 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +708 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +35 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +209 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +231 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +408 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +92 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +74 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +366 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +37 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +363 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +133 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +74 -0
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +118 -0
  134. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -0
  135. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
  136. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
  137. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
  138. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  139. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +217 -0
  140. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +86 -0
  141. package/gradle/libs.versions.toml +69 -0
  142. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  143. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  144. package/gradle.properties +26 -0
  145. package/gradlew +252 -0
  146. package/gradlew.bat +94 -0
  147. package/ios/Application/ApplicationEnvironment.swift +11 -0
  148. package/ios/Application/ComposeApi.swift +2 -1
  149. package/ios/Application/{Components.swift → LegacyHeader.swift} +6 -62
  150. package/ios/Application/Screen.swift +3 -2
  151. package/ios/Avatar/Avatar.swift +4 -3
  152. package/ios/Badge/Badge.swift +6 -4
  153. package/ios/Badge/BadgeDot.swift +1 -1
  154. package/ios/Badge/BadgeRibbon.swift +0 -1
  155. package/ios/Button/Button.swift +15 -12
  156. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +12 -20
  157. package/ios/Carousel/Carousel.swift +29 -12
  158. package/ios/Checkbox/Checkbox.swift +7 -3
  159. package/ios/Chip/Chip.swift +5 -4
  160. package/ios/Collapse/Collapse.swift +70 -19
  161. package/ios/Colors+Radius+Spacing/Colors.swift +9 -0
  162. package/ios/DateTimePicker/DateTimePicker.swift +54 -20
  163. package/ios/DateTimePicker/WheelPicker.swift +103 -105
  164. package/ios/Divider/Divider.swift +37 -4
  165. package/ios/Extensions/Color++.swift +18 -0
  166. package/ios/Icon/Icon.swift +7 -1
  167. package/ios/IconButton/IconButton.swift +15 -7
  168. package/ios/Image/Image.swift +7 -34
  169. package/ios/Information/Information.swift +27 -9
  170. package/ios/Input/ErrorView.swift +2 -2
  171. package/ios/Input/Input.swift +160 -147
  172. package/ios/Input/InputPhoneNumber.swift +12 -9
  173. package/ios/Input/InputSearch.swift +102 -75
  174. package/ios/Input/InputTextArea.swift +128 -145
  175. package/ios/InputDropDown/InputDropDown.swift +10 -7
  176. package/ios/InputMoney/InputMoney.swift +33 -11
  177. package/ios/InputOTP/InputOTP.swift +22 -12
  178. package/ios/Layout/Card.swift +50 -0
  179. package/ios/Layout/GridContext.swift +123 -3
  180. package/ios/Layout/Item.swift +5 -4
  181. package/ios/Layout/Section.swift +4 -100
  182. package/ios/Loader/Loader.swift +5 -9
  183. package/ios/Lottie/LottieView.swift +10 -0
  184. package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +61 -9
  185. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTab.swift +60 -34
  186. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +11 -6
  187. package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
  188. package/ios/Navigation/Component/Header.swift +493 -0
  189. package/ios/Navigation/Component/HeaderBackground.swift +134 -0
  190. package/ios/Navigation/Component/HeaderJourney.swift +73 -0
  191. package/ios/Navigation/Component/HeaderLocation.swift +48 -0
  192. package/ios/Navigation/Component/HeaderRight.swift +386 -0
  193. package/ios/Navigation/Component/HeaderTitle.swift +36 -0
  194. package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +20 -23
  195. package/ios/{Application/Navigation/component → Navigation/Component}/SideBar.swift +49 -28
  196. package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
  197. package/ios/Navigation/HostBackGestureBridge.swift +57 -0
  198. package/ios/Navigation/Navigation.swift +190 -0
  199. package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +46 -41
  200. package/ios/{Application/Navigation → Navigation}/Navigator.swift +65 -9
  201. package/ios/{Application → Navigation}/StackScreen.swift +236 -103
  202. package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
  203. package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
  204. package/ios/Pagination/PaginationScroll.swift +2 -2
  205. package/ios/Popup/PopupDisplay.swift +79 -159
  206. package/ios/Popup/PopupInput.swift +10 -13
  207. package/ios/Popup/PopupLayout.swift +97 -2
  208. package/ios/Popup/PopupNotify.swift +27 -55
  209. package/ios/Popup/PopupPromotion.swift +22 -50
  210. package/ios/PopupView/FullscreenPopup.swift +13 -19
  211. package/ios/PopupView/PopupView.swift +1 -1
  212. package/ios/PopupView/Utils++.swift +0 -8
  213. package/ios/ProgressInfo/ProgressInfo.swift +4 -2
  214. package/ios/Radio/Radio.swift +2 -1
  215. package/ios/Rating/Rating.swift +9 -1
  216. package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
  217. package/ios/Skeleton/Skeleton.swift +22 -27
  218. package/ios/Slider/Slider.swift +16 -18
  219. package/ios/Stepper/Stepper.swift +6 -1
  220. package/ios/Steps/Steps.swift +8 -6
  221. package/ios/SuggestAction/SuggestAction.swift +5 -0
  222. package/ios/Swipeable/SwipeCell.swift +10 -6
  223. package/ios/Switch/Switch.swift +22 -20
  224. package/ios/TabView/TabView.swift +102 -21
  225. package/ios/Tag/Tag.swift +15 -4
  226. package/ios/Template/Logo/Logo.swift +2 -1
  227. package/ios/Template/TrustBanner/TrustBanner.swift +41 -27
  228. package/ios/Title/Title.swift +6 -17
  229. package/ios/Tooltip/Tooltip.swift +29 -14
  230. package/ios/Typography/Text.swift +61 -19
  231. package/ios/Uploader/Uploader.swift +3 -1
  232. package/ios/native-kits.podspec +1 -0
  233. package/ios-demo/README.md +120 -0
  234. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
  235. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
  236. package/package.json +1 -1
  237. package/settings.gradle.kts +64 -0
  238. package/ios/Application/HeaderRight.swift +0 -324
  239. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  240. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  241. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  242. package/ios/Application/Navigation/NavigatorManager.swift +0 -29
  243. package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -54
  244. package/ios/Application/Navigation/component/Header.swift +0 -183
  245. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -84
  246. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  247. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
  248. package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
  249. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -280
  250. /package/ios/{Application/Layout → Layout}/ScreenLayout.swift +0 -0
  251. /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
  252. /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoBarCompression.swift +0 -0
  253. /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoTabBar.swift +0 -0
  254. /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 leave the header bar for a column in the side safe area,
6
- // as the system bars do. Port of RN `SideBarButton.tsx` + `useSideBarItemStyle`.
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
- tintColor: headerColor.tintIconColor,
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
- ToolkitNavigationHeaderRight(
55
- headerRight: toolkit.data,
56
- tintColor: toolkit.tintColor ?? headerColor.tintIconColor,
57
- vertical: true
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 tintColor: Color?
98
+ let headerColor: HeaderColor
79
99
  let shape: S
80
100
 
81
- private var isDark: Bool { tintColor == Colors.black01 || tintColor == .white }
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
- var tintColor: Color?
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: { if !disabled { action() } }) {
110
- Icon(source: icon, size: sideBarIconSize, color: tintColor ?? Colors.black17)
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(tintColor: tintColor, shape: Circle()))
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(.plain)
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
- var tintColor: Color?
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(tintColor: tintColor, shape: RoundedRectangle(cornerRadius: Radius.XL)))
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
- var tintColor: Color?
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: tintColor ?? Colors.black17)
169
+ Icon(source: icon, size: sideBarIconSize, color: headerColor.tintIconColor)
149
170
  .frame(maxWidth: .infinity, maxHeight: .infinity)
150
171
  .contentShape(Rectangle())
151
172
  }
152
- .buttonStyle(.plain)
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 `SnackBar` (navigation/component/SnackBar.kt).
6
- // A snack bar slides up from the bottom (just above the footer / home
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
- /// Bottom inset so the bar sits above the footer / home indicator
57
- /// (mirrors Compose's `footerHeight` offset).
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
- // Wraps NavigationStack (iOS 16+), injects Navigator into the environment, and renders
7
- // every registered screen inside `StackScreen` chrome built from its options.
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
- // Register with the host swipe-back bridge (mirrors ComposeAppContainer's
81
- // ComposeNavigatorManager.push). Idempotent, so a re-appear won't double-count.
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
- NavigatorManager.shared.remove(navigator)
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
- let options = screen.options ?? NavigationOptions()
149
- StackScreen(
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
- content: { screen.content() }
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: safeAreaBottomInset() + Spacing.S)
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
- }