@momo-kits/native-kits 0.162.2-beta.1 → 0.162.2-sp.1-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 (295) hide show
  1. package/.claude/settings.local.json +55 -0
  2. package/build.gradle.kts +11 -0
  3. package/compose/build.gradle.kts +186 -0
  4. package/compose/build.gradle.kts.backup +186 -0
  5. package/compose/compose.podspec +47 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +123 -0
  8. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  9. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  10. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +109 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +305 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +921 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +121 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +403 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +85 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +340 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +198 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +123 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +94 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +136 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +224 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +148 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +188 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +116 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +452 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +172 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +255 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +233 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +241 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +92 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +352 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +103 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +338 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +70 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +96 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +348 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +256 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +494 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +131 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +215 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +96 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +531 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +92 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +135 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +214 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +590 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +177 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +192 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +205 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +239 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +285 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +57 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +14 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +50 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +253 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +133 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +104 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +145 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +345 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +556 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +161 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +243 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +187 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +315 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +370 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +131 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +167 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +72 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +66 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +70 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +33 -0
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +186 -0
  129. package/gradle/libs.versions.toml +67 -0
  130. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  131. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  132. package/gradle.properties +26 -0
  133. package/gradlew +252 -0
  134. package/gradlew.bat +94 -0
  135. package/ios/Application/Components.swift +34 -43
  136. package/ios/Application/FloatingButton.swift +10 -8
  137. package/ios/Application/Navigation/BottomTab/BottomTab.swift +102 -0
  138. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  139. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  140. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  141. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  142. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  143. package/ios/Application/Navigation/NavigationContainer.swift +151 -0
  144. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  145. package/ios/Application/Navigation/Navigator.swift +360 -0
  146. package/ios/Application/Navigation/Overplay/BottomSheet.swift +176 -0
  147. package/ios/Application/Navigation/Overplay/ModalScreen.swift +68 -0
  148. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  149. package/ios/Application/Navigation/component/Header.swift +178 -0
  150. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  151. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  152. package/ios/Application/StackScreen.swift +449 -0
  153. package/ios/Avatar/Avatar.swift +193 -0
  154. package/ios/Badge/Badge.swift +11 -13
  155. package/ios/Badge/BadgeRibbon.swift +12 -19
  156. package/ios/BaselineView/BaselineView.swift +163 -0
  157. package/ios/Button/Button.swift +62 -23
  158. package/ios/Carousel/Carousel.swift +181 -0
  159. package/ios/Checkbox/Checkbox.swift +14 -12
  160. package/ios/Chip/Chip.swift +23 -9
  161. package/ios/Collapse/Collapse.swift +182 -0
  162. package/ios/Colors+Radius+Spacing/Colors.swift +1 -1
  163. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  164. package/ios/DateTimePicker/DateTimePicker.swift +210 -0
  165. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  166. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  167. package/ios/DateTimePicker/WheelPicker.swift +161 -0
  168. package/ios/Divider/Divider.swift +16 -0
  169. package/ios/Icon/Icon.swift +6 -1
  170. package/ios/IconButton/IconButton.swift +112 -0
  171. package/ios/Information/Information.swift +151 -0
  172. package/ios/Input/Input.swift +42 -25
  173. package/ios/Input/InputPhoneNumber.swift +21 -19
  174. package/ios/Input/InputSearch.swift +101 -45
  175. package/ios/Input/InputTextArea.swift +67 -40
  176. package/ios/InputDropDown/InputDropDown.swift +198 -0
  177. package/ios/InputMoney/InputMoney.swift +335 -0
  178. package/ios/InputOTP/InputOTP.swift +210 -0
  179. package/ios/Loader/Loader.swift +113 -0
  180. package/ios/MoMoUIKits.podspec +5 -0
  181. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  182. package/ios/Pagination/PaginationDot.swift +61 -0
  183. package/ios/Pagination/PaginationNumber.swift +35 -0
  184. package/ios/Pagination/PaginationScroll.swift +101 -0
  185. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  186. package/ios/Popup/PopupDisplay.swift +10 -12
  187. package/ios/Popup/PopupInput.swift +2 -6
  188. package/ios/Popup/PopupNotify.swift +219 -0
  189. package/ios/ProgressInfo/ProgressInfo.swift +294 -0
  190. package/ios/Radio/Radio.swift +70 -0
  191. package/ios/Rating/Rating.swift +82 -0
  192. package/ios/Skeleton/Skeleton.swift +99 -0
  193. package/ios/Slider/Slider.swift +237 -0
  194. package/ios/Stepper/Stepper.swift +220 -0
  195. package/ios/Steps/Steps.swift +451 -0
  196. package/ios/SuggestAction/SuggestAction.swift +97 -0
  197. package/ios/Swipeable/SwipeCell.swift +185 -0
  198. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  199. package/ios/TabView/TabView.swift +530 -0
  200. package/ios/Tag/Tag.swift +98 -0
  201. package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
  202. package/ios/Title/Title.swift +269 -0
  203. package/ios/Tooltip/Tooltip.swift +482 -0
  204. package/ios/Typography/Text.swift +86 -24
  205. package/ios/Typography/Typography.swift +31 -6
  206. package/ios/Uploader/Uploader.swift +183 -0
  207. package/ios/native-kits.podspec +58 -8
  208. package/ios-demo/MoMoUIKitsDemo.podspec +18 -0
  209. package/ios-demo/README.md +120 -0
  210. package/ios-demo/Resources/dot_loading.json +558 -0
  211. package/ios-demo/Resources/icon.json +4052 -0
  212. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  213. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  214. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  215. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  216. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  217. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  218. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  219. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  220. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  221. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  222. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  223. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +72 -0
  224. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  225. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  226. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  227. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  228. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  229. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  230. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  231. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +63 -0
  232. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  233. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  234. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  235. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  236. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  237. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  238. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +261 -0
  239. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  240. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  241. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  242. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  243. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  244. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  245. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  246. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  247. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  248. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  249. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  250. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +227 -0
  251. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  252. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  253. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  254. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  255. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  256. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  257. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  258. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  259. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  260. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  261. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  262. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  263. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  264. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  265. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  266. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  267. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  268. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  269. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  270. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  271. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  272. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  273. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +50 -0
  274. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  275. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  276. package/local.properties +8 -0
  277. package/package.json +1 -1
  278. package/publish.sh +50 -0
  279. package/scripts/gen-ios-color.mjs +136 -0
  280. package/settings.gradle.kts +64 -0
  281. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +0 -32
  282. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  283. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  284. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +0 -16
  285. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  286. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  287. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  288. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  289. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  290. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  291. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  292. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +0 -58
  293. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +0 -58
  294. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -60
  295. package/ios/Typography/MomoFontScale.swift +0 -36
@@ -0,0 +1,151 @@
1
+ //
2
+ // NavigationContainer.swift
3
+ // MoMoUIKits
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.
9
+ //
10
+
11
+ import SwiftUI
12
+
13
+ @available(iOS 16.0, *)
14
+ public struct NavigationContainer<Initial: View>: View {
15
+ @StateObject private var navigator: Navigator
16
+
17
+ private let setNavigator: ((Navigator) -> Void)?
18
+
19
+ public init(
20
+ initialScreenName: String = "",
21
+ @ViewBuilder initialScreen: @escaping () -> Initial,
22
+ options: NavigationOptions? = nil,
23
+ setNavigator: ((Navigator) -> Void)? = nil
24
+ ) {
25
+ let nav = Navigator(
26
+ initialScreenName: initialScreenName.isEmpty ? (options?.screenName ?? "") : initialScreenName,
27
+ initialContent: { AnyView(initialScreen()) },
28
+ options: options
29
+ )
30
+ _navigator = StateObject(wrappedValue: nav)
31
+ self.setNavigator = setNavigator
32
+ }
33
+
34
+ public var body: some View {
35
+ NavigationStack(path: $navigator.path) {
36
+ screenView(for: navigator.rootRoute)
37
+ .navigationDestination(for: DynamicScreenRoute.self) { route in
38
+ screenView(for: route)
39
+ }
40
+ }
41
+ .environmentObject(navigator)
42
+ .fullScreenCover(item: $navigator.presented) { route in
43
+ screenView(forDialog: route)
44
+ .environmentObject(navigator)
45
+ }
46
+ .overlay {
47
+ if let item = navigator.overplay {
48
+ OverplayHost(item: item)
49
+ .environmentObject(navigator)
50
+ }
51
+ }
52
+ .onAppear { setNavigator?(navigator) }
53
+ }
54
+
55
+ @ViewBuilder
56
+ private func screenView(for route: DynamicScreenRoute) -> some View {
57
+ if let screen = DynamicScreenRegistry.shared.getScreen(id: route.id) {
58
+ ScreenHost(screen: screen, navigator: navigator)
59
+ .navigationBarBackButtonHidden(true)
60
+ } else {
61
+ EmptyView()
62
+ }
63
+ }
64
+
65
+ @ViewBuilder
66
+ private func screenView(forDialog route: DynamicDialogRoute) -> some View {
67
+ if let screen = DynamicScreenRegistry.shared.getScreen(id: route.id) {
68
+ ScreenHost(screen: screen, navigator: navigator)
69
+ } else {
70
+ EmptyView()
71
+ }
72
+ }
73
+ }
74
+
75
+ // MARK: - ScreenHost
76
+
77
+ @available(iOS 16.0, *)
78
+ private struct ScreenHost: View {
79
+ let screen: DynamicScreen
80
+ let navigator: Navigator
81
+
82
+ var body: some View {
83
+ let options = screen.options ?? NavigationOptions()
84
+ StackScreen(
85
+ backgroundColor: options.backgroundColor,
86
+ headerTransparent: options.headerTransparent,
87
+ fullScreenContent: options.fullScreenContent,
88
+ tintColor: options.tintColor ?? Colors.black17,
89
+ isBack: !options.hiddenBack,
90
+ headerType: options.headerType,
91
+ useAnimated: options.useAnimated,
92
+ title: options.title,
93
+ headerTitle: options.headerTitle,
94
+ titlePosition: options.titlePosition,
95
+ goBack: options.hiddenBack ? nil : { navigator.onBackSafe() },
96
+ scrollable: options.scrollable,
97
+ scrollToTopCallback: options.scrollToTopCallback,
98
+ useAvoidKeyboard: options.useAvoidKeyboard,
99
+ keyboardShouldPersistTaps: options.keyboardShouldPersistTaps,
100
+ footer: options.footer,
101
+ headerRight: options.resolvedHeaderRight,
102
+ animatedHeader: options.animatedHeader,
103
+ layoutOffset: options.layoutOffset,
104
+ inputSearchProps: options.inputSearchProps,
105
+ fabProps: options.fabProps,
106
+ content: { screen.content() }
107
+ )
108
+ }
109
+ }
110
+
111
+ // MARK: - Overplay host
112
+
113
+ @available(iOS 16.0, *)
114
+ private struct OverplayHost: View {
115
+ let item: OverplayItem
116
+ @EnvironmentObject var navigator: Navigator
117
+
118
+ var body: some View {
119
+ // Each component self-animates on appear and drives its own exit through
120
+ // the Navigator before the item is removed (mirrors Compose's overplay).
121
+ switch item.type {
122
+ case .modal:
123
+ ModalScreen(barrierDismissible: item.barrierDismissible) {
124
+ item.content()
125
+ }
126
+ .id(item.id)
127
+ case .bottomSheet:
128
+ BottomSheetView(
129
+ header: item.header ?? .title(),
130
+ isSurface: item.isSurface,
131
+ barrierDismissible: item.barrierDismissible
132
+ ) {
133
+ item.content()
134
+ }
135
+ .id(item.id)
136
+ case .snackBar:
137
+ if let snackBar = item.snackBar {
138
+ SnackBarView(snackBar: snackBar, bottomInset: safeAreaBottomInset() + Spacing.S)
139
+ .id(item.id)
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ /// Bottom safe-area inset (home indicator), used to lift the snack bar.
146
+ @available(iOS 16.0, *)
147
+ private func safeAreaBottomInset() -> CGFloat {
148
+ UIApplication.shared.connectedScenes
149
+ .compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
150
+ .first ?? 0
151
+ }
@@ -0,0 +1,109 @@
1
+ //
2
+ // NavigationOptions.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's NavigationOptions.
6
+ // Each option lines up with a parameter on the existing `Screen` view,
7
+ // so NavigationContainer can auto-wrap a registered screen body in `Screen(options)`.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ public struct NavigationOptions {
13
+ public var screenName: String?
14
+ public var title: String
15
+ /// Rich header title (mirrors Compose's `headerTitle`). When set to `.user`
16
+ /// the header renders an avatar group instead of `title`; `.default` keeps
17
+ /// the plain `title` text.
18
+ public var headerTitle: HeaderTitle?
19
+ public var titlePosition: TitlePosition
20
+ public var headerType: HeaderType
21
+ /// Mirrors Compose's `HeaderType.Extended/Default(useAnimated = ...)`: when true
22
+ /// and `inputSearchProps` is set, the search bar animates into the header on scroll.
23
+ public var useAnimated: Bool
24
+ public var headerTransparent: Bool
25
+ public var fullScreenContent: Bool
26
+ public var hiddenBack: Bool
27
+ public var backgroundColor: Color?
28
+ public var tintColor: Color?
29
+ public var scrollable: Bool
30
+ /// Mirrors Compose's `ScrollData.scrollToTopCallback`: invoked when the
31
+ /// screen is asked to scroll to top (e.g. on a status-bar tap).
32
+ public var scrollToTopCallback: (() -> Void)?
33
+ public var useAvoidKeyboard: Bool
34
+ /// Mirrors Compose's `KeyboardOptions.keyboardShouldPersistTaps`: when true,
35
+ /// tapping anywhere on the screen dismisses the keyboard.
36
+ public var keyboardShouldPersistTaps: Bool
37
+ /// Mirrors Compose's `HeaderBackProps`: drives the prevent-back confirm popup.
38
+ public var headerBackProps: HeaderBackProps?
39
+ /// Typed toolkit data for the default header right control (mirrors
40
+ /// Compose's `HeaderRight.Toolkit`). Use `headerRight` for `.custom` and nil
41
+ /// for `.none`.
42
+ public var headerRightData: HeaderRightData?
43
+ public var headerRight: (() -> any View)?
44
+ public var footer: (() -> AnyView)?
45
+ public var animatedHeader: AnimatedHeader?
46
+ public var layoutOffset: CGFloat
47
+ public var inputSearchProps: InputSearchProps?
48
+ public var fabProps: FabProps?
49
+ public var onBackHandler: (() -> Void)?
50
+
51
+ public init(
52
+ screenName: String? = nil,
53
+ title: String = "Stack",
54
+ headerTitle: HeaderTitle? = nil,
55
+ titlePosition: TitlePosition = .left,
56
+ headerType: HeaderType = .default,
57
+ useAnimated: Bool = false,
58
+ headerTransparent: Bool = false,
59
+ fullScreenContent: Bool = false,
60
+ hiddenBack: Bool = false,
61
+ backgroundColor: Color? = nil,
62
+ tintColor: Color? = Colors.black17,
63
+ scrollable: Bool = true,
64
+ scrollToTopCallback: (() -> Void)? = nil,
65
+ useAvoidKeyboard: Bool = true,
66
+ keyboardShouldPersistTaps: Bool = false,
67
+ headerBackProps: HeaderBackProps? = nil,
68
+ headerRightData: HeaderRightData? = nil,
69
+ headerRight: (() -> any View)? = { HeaderRight() },
70
+ footer: (() -> AnyView)? = nil,
71
+ animatedHeader: AnimatedHeader? = nil,
72
+ layoutOffset: CGFloat = 56,
73
+ inputSearchProps: InputSearchProps? = nil,
74
+ fabProps: FabProps? = nil,
75
+ onBackHandler: (() -> Void)? = nil
76
+ ) {
77
+ self.screenName = screenName
78
+ self.title = title
79
+ self.headerTitle = headerTitle
80
+ self.titlePosition = titlePosition
81
+ self.headerType = headerType
82
+ self.useAnimated = useAnimated
83
+ self.headerTransparent = headerTransparent
84
+ self.fullScreenContent = fullScreenContent
85
+ self.hiddenBack = hiddenBack
86
+ self.backgroundColor = backgroundColor
87
+ self.tintColor = tintColor
88
+ self.scrollable = scrollable
89
+ self.scrollToTopCallback = scrollToTopCallback
90
+ self.useAvoidKeyboard = useAvoidKeyboard
91
+ self.keyboardShouldPersistTaps = keyboardShouldPersistTaps
92
+ self.headerBackProps = headerBackProps
93
+ self.headerRightData = headerRightData
94
+ self.headerRight = headerRight
95
+ self.footer = footer
96
+ self.animatedHeader = animatedHeader
97
+ self.layoutOffset = layoutOffset
98
+ self.inputSearchProps = inputSearchProps
99
+ self.fabProps = fabProps
100
+ self.onBackHandler = onBackHandler
101
+ }
102
+ }
103
+
104
+ extension NavigationOptions {
105
+ var resolvedHeaderRight: (() -> any View)? {
106
+ guard let headerRightData else { return headerRight }
107
+ return { HeaderRight(headerRight: headerRightData, tintColor: tintColor) }
108
+ }
109
+ }
@@ -0,0 +1,360 @@
1
+ //
2
+ // Navigator.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's Navigator + DynamicScreenRegistry.
6
+ // Screens are registered by Int id; the NavigationStack path stores routes
7
+ // by id and looks up content from the registry on render.
8
+ //
9
+
10
+ import SwiftUI
11
+ import Combine
12
+
13
+ // MARK: - Routes
14
+
15
+ public struct DynamicScreenRoute: Hashable, Identifiable {
16
+ public let id: Int
17
+ }
18
+
19
+ public struct DynamicDialogRoute: Hashable, Identifiable {
20
+ public let id: Int
21
+ }
22
+
23
+ // MARK: - Registry
24
+
25
+ public struct DynamicScreen {
26
+ public let id: Int
27
+ public let name: String
28
+ public let content: () -> AnyView
29
+ public var options: NavigationOptions?
30
+ }
31
+
32
+ public final class DynamicScreenRegistry {
33
+ public static let shared = DynamicScreenRegistry()
34
+
35
+ private var screens: [Int: DynamicScreen] = [:]
36
+ private var idCounter: Int = 1
37
+
38
+ private init() {}
39
+
40
+ @discardableResult
41
+ public func register(
42
+ screenName: String = "",
43
+ content: @escaping () -> AnyView,
44
+ options: NavigationOptions? = nil
45
+ ) -> DynamicScreenRoute {
46
+ let id = idCounter
47
+ idCounter += 1
48
+ screens[id] = DynamicScreen(id: id, name: screenName, content: content, options: options)
49
+ return DynamicScreenRoute(id: id)
50
+ }
51
+
52
+ public func unregister(id: Int) {
53
+ screens.removeValue(forKey: id)
54
+ }
55
+
56
+ public func unregisterAll() {
57
+ screens.removeAll()
58
+ }
59
+
60
+ public func getScreen(id: Int) -> DynamicScreen? { screens[id] }
61
+
62
+ public func latest() -> DynamicScreen? {
63
+ screens.max(by: { $0.key < $1.key })?.value
64
+ }
65
+
66
+ public func setOptions(id: Int, options: NavigationOptions) {
67
+ screens[id]?.options = options
68
+ }
69
+ }
70
+
71
+ // MARK: - Overplay
72
+
73
+ public enum OverplayType {
74
+ case modal, bottomSheet, snackBar
75
+ }
76
+
77
+ public struct OverplayItem: Identifiable {
78
+ public let id = UUID()
79
+ public let type: OverplayType
80
+ public let content: () -> AnyView
81
+ public let barrierDismissible: Bool
82
+ public let onDismiss: (() -> Void)?
83
+ // Bottom-sheet params (mirrors Compose's OverplayComponentParams.BottomSheet).
84
+ public var isSurface: Bool = false
85
+ public var header: BottomSheetHeader? = nil
86
+ // Snack-bar payload (mirrors Compose's SnackBar value).
87
+ public var snackBar: SnackBar? = nil
88
+ }
89
+
90
+ // MARK: - Navigator
91
+
92
+ @available(iOS 16.0, *)
93
+ public final class Navigator: ObservableObject {
94
+ @Published public var path: [DynamicScreenRoute] = []
95
+ @Published public var presented: DynamicDialogRoute?
96
+ @Published public var overplay: OverplayItem?
97
+
98
+ /// Animated-close handler registered by the active overplay component
99
+ /// (mirrors Compose's `OverplayComponentRegistry.bindClose`). When set, a
100
+ /// dismiss request runs the component's exit animation before the item is
101
+ /// actually removed via `finishOverplayDismiss`.
102
+ private var overplayDismissHandler: (() -> Void)?
103
+
104
+ public private(set) var rootRoute: DynamicScreenRoute
105
+
106
+ public init(
107
+ initialScreenName: String = "",
108
+ initialContent: @escaping () -> AnyView,
109
+ options: NavigationOptions? = nil
110
+ ) {
111
+ self.rootRoute = DynamicScreenRegistry.shared.register(
112
+ screenName: initialScreenName,
113
+ content: initialContent,
114
+ options: options
115
+ )
116
+ }
117
+
118
+ // MARK: Stack
119
+
120
+ public func push<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
121
+ let route = DynamicScreenRegistry.shared.register(
122
+ screenName: options?.screenName ?? "",
123
+ content: { AnyView(content()) },
124
+ options: options
125
+ )
126
+ path.append(route)
127
+ }
128
+
129
+ public func push<V: View>(
130
+ screenName: String,
131
+ @ViewBuilder _ content: @escaping () -> V,
132
+ options: NavigationOptions? = nil
133
+ ) {
134
+ let route = DynamicScreenRegistry.shared.register(
135
+ screenName: screenName,
136
+ content: { AnyView(content()) },
137
+ options: options
138
+ )
139
+ path.append(route)
140
+ }
141
+
142
+ public func replace<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
143
+ guard !path.isEmpty else { return }
144
+ let removed = path.removeLast()
145
+ DynamicScreenRegistry.shared.unregister(id: removed.id)
146
+ push(content, options: options)
147
+ }
148
+
149
+ public func replace<V: View>(
150
+ screenName: String,
151
+ @ViewBuilder _ content: @escaping () -> V,
152
+ options: NavigationOptions? = nil
153
+ ) {
154
+ guard !path.isEmpty else { return }
155
+ let removed = path.removeLast()
156
+ DynamicScreenRegistry.shared.unregister(id: removed.id)
157
+ push(screenName: screenName, content, options: options)
158
+ }
159
+
160
+ public func reset<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
161
+ for route in path { DynamicScreenRegistry.shared.unregister(id: route.id) }
162
+ DynamicScreenRegistry.shared.unregister(id: rootRoute.id)
163
+ path.removeAll()
164
+ let route = DynamicScreenRegistry.shared.register(
165
+ screenName: options?.screenName ?? "",
166
+ content: { AnyView(content()) },
167
+ options: options
168
+ )
169
+ rootRoute = route
170
+ }
171
+
172
+ public func reset<V: View>(
173
+ screenName: String,
174
+ @ViewBuilder _ content: @escaping () -> V,
175
+ options: NavigationOptions? = nil
176
+ ) {
177
+ for route in path { DynamicScreenRegistry.shared.unregister(id: route.id) }
178
+ DynamicScreenRegistry.shared.unregister(id: rootRoute.id)
179
+ path.removeAll()
180
+ let route = DynamicScreenRegistry.shared.register(
181
+ screenName: screenName,
182
+ content: { AnyView(content()) },
183
+ options: options
184
+ )
185
+ rootRoute = route
186
+ }
187
+
188
+ public func pop(_ count: Int = 1, callback: (() -> Void)? = nil) {
189
+ var remaining = count
190
+ while remaining > 0 {
191
+ if overplay != nil {
192
+ hideOverplay()
193
+ } else if presented != nil {
194
+ let dismissedId = presented?.id
195
+ presented = nil
196
+ if let id = dismissedId { DynamicScreenRegistry.shared.unregister(id: id) }
197
+ } else if !path.isEmpty {
198
+ let removed = path.removeLast()
199
+ DynamicScreenRegistry.shared.unregister(id: removed.id)
200
+ } else {
201
+ break
202
+ }
203
+ remaining -= 1
204
+ }
205
+ callback?()
206
+ }
207
+
208
+ public func popToRoot() {
209
+ for route in path { DynamicScreenRegistry.shared.unregister(id: route.id) }
210
+ path.removeAll()
211
+ }
212
+
213
+ public func onBackSafe(callback: (() -> Void)? = nil) {
214
+ let latest = DynamicScreenRegistry.shared.latest()
215
+ if let onBack = latest?.options?.onBackHandler {
216
+ onBack()
217
+ return
218
+ }
219
+ if let preventBack = latest?.options?.headerBackProps?.preventBack {
220
+ showModal {
221
+ PopupDisplay(
222
+ isPresented: .constant(true),
223
+ title: preventBack.title,
224
+ description: preventBack.description,
225
+ url: preventBack.image,
226
+ buttonDirection: preventBack.buttonDirection,
227
+ buttonType: .button,
228
+ actionButtonTitle: preventBack.primary?.title ?? "",
229
+ closeButtonTitle: preventBack.secondary?.title ?? "",
230
+ onPressAction: { [weak self] in
231
+ preventBack.primary?.onPress?()
232
+ // Close the popup, then leave the screen (matches Compose's pop(2)).
233
+ self?.pop(2, callback: callback)
234
+ },
235
+ onPressCloseButton: { [weak self] in
236
+ preventBack.secondary?.onPress?()
237
+ self?.hideOverplay()
238
+ },
239
+ onClose: { [weak self] in
240
+ preventBack.onIconClose?()
241
+ self?.hideOverplay()
242
+ },
243
+ errorCode: preventBack.error,
244
+ isShowCloseIcon: preventBack.isShowCloseIcon
245
+ )
246
+ }
247
+ return
248
+ }
249
+ pop(1, callback: callback)
250
+ }
251
+
252
+ // MARK: Modal-style screen (vertical slide)
253
+
254
+ public func present<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
255
+ let route = DynamicScreenRegistry.shared.register(
256
+ screenName: options?.screenName ?? "",
257
+ content: { AnyView(content()) },
258
+ options: options
259
+ )
260
+ presented = DynamicDialogRoute(id: route.id)
261
+ }
262
+
263
+ public func present<V: View>(
264
+ screenName: String,
265
+ @ViewBuilder _ content: @escaping () -> V,
266
+ options: NavigationOptions? = nil
267
+ ) {
268
+ let route = DynamicScreenRegistry.shared.register(
269
+ screenName: screenName,
270
+ content: { AnyView(content()) },
271
+ options: options
272
+ )
273
+ presented = DynamicDialogRoute(id: route.id)
274
+ }
275
+
276
+ public func dismiss() {
277
+ if let id = presented?.id {
278
+ DynamicScreenRegistry.shared.unregister(id: id)
279
+ }
280
+ presented = nil
281
+ }
282
+
283
+ // MARK: Overplay
284
+
285
+ public func showModal<V: View>(
286
+ @ViewBuilder _ content: @escaping () -> V,
287
+ barrierDismissible: Bool = true,
288
+ onDismiss: (() -> Void)? = nil
289
+ ) {
290
+ overplayDismissHandler = nil
291
+ overplay = OverplayItem(
292
+ type: .modal,
293
+ content: { AnyView(content()) },
294
+ barrierDismissible: barrierDismissible,
295
+ onDismiss: onDismiss
296
+ )
297
+ }
298
+
299
+ public func showBottomSheet<V: View>(
300
+ @ViewBuilder _ content: @escaping () -> V,
301
+ isSurface: Bool = false,
302
+ barrierDismissible: Bool = true,
303
+ header: BottomSheetHeader? = nil,
304
+ onDismiss: (() -> Void)? = nil
305
+ ) {
306
+ overplayDismissHandler = nil
307
+ overplay = OverplayItem(
308
+ type: .bottomSheet,
309
+ content: { AnyView(content()) },
310
+ barrierDismissible: barrierDismissible,
311
+ onDismiss: onDismiss,
312
+ isSurface: isSurface,
313
+ header: header
314
+ )
315
+ }
316
+
317
+ /// Mirrors Compose's `showSnackBar(snackBar:)`.
318
+ public func showSnackBar(_ snackBar: SnackBar, onDismiss: (() -> Void)? = nil) {
319
+ overplayDismissHandler = nil
320
+ overplay = OverplayItem(
321
+ type: .snackBar,
322
+ content: { AnyView(EmptyView()) },
323
+ barrierDismissible: false,
324
+ onDismiss: onDismiss,
325
+ snackBar: snackBar
326
+ )
327
+ }
328
+
329
+ // MARK: Overplay dismissal
330
+
331
+ /// Registered by the active overplay component so external dismiss requests
332
+ /// run its exit animation (mirrors Compose's `bindClose`).
333
+ public func registerOverplayDismiss(_ handler: @escaping () -> Void) {
334
+ overplayDismissHandler = handler
335
+ }
336
+
337
+ /// Removes the overplay and fires its `onDismiss` (mirrors Compose's
338
+ /// `hardClearAfterDismiss`). Called by a component once its exit animation
339
+ /// has completed.
340
+ public func finishOverplayDismiss() {
341
+ let dismissCallback = overplay?.onDismiss
342
+ overplayDismissHandler = nil
343
+ overplay = nil
344
+ dismissCallback?()
345
+ }
346
+
347
+ public func hideOverplay() {
348
+ if let close = overplayDismissHandler {
349
+ // Component runs its exit animation, then calls finishOverplayDismiss.
350
+ overplayDismissHandler = nil
351
+ close()
352
+ } else {
353
+ finishOverplayDismiss()
354
+ }
355
+ }
356
+
357
+ public func hideSnackBar() {
358
+ if overplay?.type == .snackBar { hideOverplay() }
359
+ }
360
+ }