@momo-kits/native-kits 0.163.1-beta.3 → 0.163.1-beta.5-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 (331) hide show
  1. package/CLAUDE.md +1 -1
  2. package/build.gradle.kts +11 -0
  3. package/compose/build.gradle.kts +190 -0
  4. package/compose/build.gradle.kts.backup +190 -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/OsFontScale.android.kt +7 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +123 -0
  9. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  10. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +106 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +294 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +112 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +477 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +214 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +276 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +253 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +98 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +172 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +360 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +562 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +154 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +128 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +70 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +58 -0
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  132. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
  133. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  134. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +184 -0
  135. package/gradle/libs.versions.toml +69 -0
  136. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  137. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  138. package/gradle.properties +26 -0
  139. package/gradlew +252 -0
  140. package/gradlew.bat +94 -0
  141. package/ios/Application/ApplicationEnvironment.swift +11 -1
  142. package/ios/Application/Components.swift +34 -44
  143. package/ios/Application/FloatingButton.swift +10 -9
  144. package/ios/Application/HeaderRight.swift +79 -45
  145. package/ios/Application/Navigation/BottomTab/BottomTab.swift +103 -0
  146. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  147. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  148. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  149. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  150. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  151. package/ios/Application/Navigation/NavigationContainer.swift +176 -0
  152. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  153. package/ios/Application/Navigation/Navigator.swift +364 -0
  154. package/ios/Application/Navigation/Overplay/BottomSheet.swift +188 -0
  155. package/ios/Application/Navigation/Overplay/ModalScreen.swift +68 -0
  156. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  157. package/ios/Application/Navigation/component/Header.swift +178 -0
  158. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  159. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  160. package/ios/Application/Screen.swift +2 -1
  161. package/ios/Application/StackScreen.swift +453 -0
  162. package/ios/Avatar/Avatar.swift +193 -0
  163. package/ios/Badge/Badge.swift +11 -14
  164. package/ios/Badge/BadgeRibbon.swift +12 -21
  165. package/ios/BaselineView/BaselineView.swift +163 -0
  166. package/ios/Button/Button.swift +61 -22
  167. package/ios/Carousel/Carousel.swift +181 -0
  168. package/ios/Checkbox/Checkbox.swift +14 -13
  169. package/ios/Chip/Chip.swift +23 -9
  170. package/ios/Collapse/Collapse.swift +182 -0
  171. package/ios/Colors+Radius+Spacing/Colors.swift +12 -12
  172. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  173. package/ios/DateTimePicker/DateTimePicker.swift +210 -0
  174. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  175. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  176. package/ios/DateTimePicker/WheelPicker.swift +161 -0
  177. package/ios/Divider/Divider.swift +16 -0
  178. package/ios/Extensions/Color++.swift +32 -0
  179. package/ios/Icon/Icon.swift +6 -1
  180. package/ios/IconButton/IconButton.swift +112 -0
  181. package/ios/Input/Input.swift +41 -25
  182. package/ios/Input/InputPhoneNumber.swift +20 -19
  183. package/ios/Input/InputSearch.swift +100 -47
  184. package/ios/Input/InputTextArea.swift +66 -40
  185. package/ios/InputDropDown/InputDropDown.swift +198 -0
  186. package/ios/InputMoney/InputMoney.swift +335 -0
  187. package/ios/InputOTP/InputOTP.swift +210 -0
  188. package/ios/Loader/Loader.swift +113 -0
  189. package/ios/MoMoUIKits.podspec +5 -0
  190. package/ios/Pagination/PaginationDot.swift +61 -0
  191. package/ios/Pagination/PaginationNumber.swift +35 -0
  192. package/ios/Pagination/PaginationScroll.swift +101 -0
  193. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  194. package/ios/Popup/PopupDisplay.swift +8 -16
  195. package/ios/Popup/PopupInput.swift +2 -8
  196. package/ios/Popup/PopupNotify.swift +219 -0
  197. package/ios/ProgressInfo/ProgressInfo.swift +294 -0
  198. package/ios/Radio/Radio.swift +70 -0
  199. package/ios/Rating/Rating.swift +82 -0
  200. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +11 -0
  201. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +38 -0
  202. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +38 -0
  203. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +38 -0
  204. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +6 -0
  205. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +38 -0
  206. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +38 -0
  207. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +38 -0
  208. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +38 -0
  209. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +38 -0
  210. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +38 -0
  211. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +38 -0
  212. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +38 -0
  213. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
  214. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
  215. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
  216. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
  217. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
  218. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
  219. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
  220. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
  221. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
  222. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
  223. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
  224. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
  225. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
  226. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
  227. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
  228. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
  229. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
  230. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
  231. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
  232. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +38 -0
  233. package/ios/Skeleton/Skeleton.swift +99 -0
  234. package/ios/Slider/Slider.swift +237 -0
  235. package/ios/Stepper/Stepper.swift +220 -0
  236. package/ios/Steps/Steps.swift +451 -0
  237. package/ios/SuggestAction/SuggestAction.swift +97 -0
  238. package/ios/Swipeable/SwipeCell.swift +185 -0
  239. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  240. package/ios/TabView/TabView.swift +530 -0
  241. package/ios/Tag/Tag.swift +98 -0
  242. package/ios/Title/Title.swift +269 -0
  243. package/ios/Tooltip/Tooltip.swift +482 -0
  244. package/ios/Typography/FontScaleStore.swift +19 -0
  245. package/ios/Typography/Text.swift +86 -17
  246. package/ios/Typography/Typography.swift +12 -14
  247. package/ios/Uploader/Uploader.swift +183 -0
  248. package/ios/native-kits.podspec +61 -8
  249. package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
  250. package/ios-demo/README.md +120 -0
  251. package/ios-demo/Resources/dot_loading.json +558 -0
  252. package/ios-demo/Resources/icon.json +4052 -0
  253. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  254. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  255. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  256. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  257. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  258. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  259. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  260. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  261. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  262. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  263. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  264. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
  265. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  266. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  267. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  268. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  269. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  270. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  271. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  272. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +63 -0
  273. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  274. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  275. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  276. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  277. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  278. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  279. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +278 -0
  280. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  281. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  282. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  283. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  284. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  285. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  286. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  287. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  288. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  289. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  290. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  291. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +227 -0
  292. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  293. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  294. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  295. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  296. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  297. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  298. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  299. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  300. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  301. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  302. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  303. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  304. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  305. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  306. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  307. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  308. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  309. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  310. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  311. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  312. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  313. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  314. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +51 -0
  315. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  316. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  317. package/local.properties +8 -0
  318. package/package.json +1 -1
  319. package/publish.sh +50 -0
  320. package/scripts/gen-ios-color.mjs +136 -0
  321. package/settings.gradle.kts +64 -0
  322. package/example/ios/Example.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  323. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  324. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  325. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  326. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  327. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  328. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  329. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  330. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -46
  331. package/ios/Application/FontScaleStore.swift +0 -59
@@ -0,0 +1,364 @@
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
+ public var composeApi: KitComposeApi?
106
+
107
+ public init(
108
+ initialScreenName: String = "",
109
+ initialContent: @escaping () -> AnyView,
110
+ options: NavigationOptions? = nil,
111
+ composeApi: KitComposeApi? = nil
112
+ ) {
113
+ self.composeApi = composeApi
114
+ self.rootRoute = DynamicScreenRegistry.shared.register(
115
+ screenName: initialScreenName,
116
+ content: initialContent,
117
+ options: options
118
+ )
119
+ }
120
+
121
+ // MARK: Stack
122
+
123
+ public func push<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
124
+ let route = DynamicScreenRegistry.shared.register(
125
+ screenName: options?.screenName ?? "",
126
+ content: { AnyView(content()) },
127
+ options: options
128
+ )
129
+ path.append(route)
130
+ }
131
+
132
+ public func push<V: View>(
133
+ screenName: String,
134
+ @ViewBuilder _ content: @escaping () -> V,
135
+ options: NavigationOptions? = nil
136
+ ) {
137
+ let route = DynamicScreenRegistry.shared.register(
138
+ screenName: screenName,
139
+ content: { AnyView(content()) },
140
+ options: options
141
+ )
142
+ path.append(route)
143
+ }
144
+
145
+ public func replace<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
146
+ guard !path.isEmpty else { return }
147
+ let removed = path.removeLast()
148
+ DynamicScreenRegistry.shared.unregister(id: removed.id)
149
+ push(content, options: options)
150
+ }
151
+
152
+ public func replace<V: View>(
153
+ screenName: String,
154
+ @ViewBuilder _ content: @escaping () -> V,
155
+ options: NavigationOptions? = nil
156
+ ) {
157
+ guard !path.isEmpty else { return }
158
+ let removed = path.removeLast()
159
+ DynamicScreenRegistry.shared.unregister(id: removed.id)
160
+ push(screenName: screenName, content, options: options)
161
+ }
162
+
163
+ public func reset<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
164
+ for route in path { DynamicScreenRegistry.shared.unregister(id: route.id) }
165
+ DynamicScreenRegistry.shared.unregister(id: rootRoute.id)
166
+ path.removeAll()
167
+ let route = DynamicScreenRegistry.shared.register(
168
+ screenName: options?.screenName ?? "",
169
+ content: { AnyView(content()) },
170
+ options: options
171
+ )
172
+ rootRoute = route
173
+ }
174
+
175
+ public func reset<V: View>(
176
+ screenName: String,
177
+ @ViewBuilder _ content: @escaping () -> V,
178
+ options: NavigationOptions? = nil
179
+ ) {
180
+ for route in path { DynamicScreenRegistry.shared.unregister(id: route.id) }
181
+ DynamicScreenRegistry.shared.unregister(id: rootRoute.id)
182
+ path.removeAll()
183
+ let route = DynamicScreenRegistry.shared.register(
184
+ screenName: screenName,
185
+ content: { AnyView(content()) },
186
+ options: options
187
+ )
188
+ rootRoute = route
189
+ }
190
+
191
+ public func pop(_ count: Int = 1, callback: (() -> Void)? = nil) {
192
+ var remaining = count
193
+ while remaining > 0 {
194
+ if overplay != nil {
195
+ hideOverplay()
196
+ } else if presented != nil {
197
+ let dismissedId = presented?.id
198
+ presented = nil
199
+ if let id = dismissedId { DynamicScreenRegistry.shared.unregister(id: id) }
200
+ } else if !path.isEmpty {
201
+ let removed = path.removeLast()
202
+ DynamicScreenRegistry.shared.unregister(id: removed.id)
203
+ } else {
204
+ composeApi?.requestCallback(funcName: "dismiss", params: nil, onResponse: nil)
205
+ break
206
+ }
207
+ remaining -= 1
208
+ }
209
+ callback?()
210
+ }
211
+
212
+ public func popToRoot() {
213
+ for route in path { DynamicScreenRegistry.shared.unregister(id: route.id) }
214
+ path.removeAll()
215
+ }
216
+
217
+ public func onBackSafe(callback: (() -> Void)? = nil) {
218
+ let latest = DynamicScreenRegistry.shared.latest()
219
+ if let onBack = latest?.options?.onBackHandler {
220
+ onBack()
221
+ return
222
+ }
223
+ if let preventBack = latest?.options?.headerBackProps?.preventBack {
224
+ showModal {
225
+ PopupDisplay(
226
+ isPresented: .constant(true),
227
+ title: preventBack.title,
228
+ description: preventBack.description,
229
+ url: preventBack.image,
230
+ buttonDirection: preventBack.buttonDirection,
231
+ buttonType: .button,
232
+ actionButtonTitle: preventBack.primary?.title ?? "",
233
+ closeButtonTitle: preventBack.secondary?.title ?? "",
234
+ onPressAction: { [weak self] in
235
+ preventBack.primary?.onPress?()
236
+ // Close the popup, then leave the screen (matches Compose's pop(2)).
237
+ self?.pop(2, callback: callback)
238
+ },
239
+ onPressCloseButton: { [weak self] in
240
+ preventBack.secondary?.onPress?()
241
+ self?.hideOverplay()
242
+ },
243
+ onClose: { [weak self] in
244
+ preventBack.onIconClose?()
245
+ self?.hideOverplay()
246
+ },
247
+ errorCode: preventBack.error,
248
+ isShowCloseIcon: preventBack.isShowCloseIcon
249
+ )
250
+ }
251
+ return
252
+ }
253
+ pop(1, callback: callback)
254
+ }
255
+
256
+ // MARK: Modal-style screen (vertical slide)
257
+
258
+ public func present<V: View>(@ViewBuilder _ content: @escaping () -> V, options: NavigationOptions? = nil) {
259
+ let route = DynamicScreenRegistry.shared.register(
260
+ screenName: options?.screenName ?? "",
261
+ content: { AnyView(content()) },
262
+ options: options
263
+ )
264
+ presented = DynamicDialogRoute(id: route.id)
265
+ }
266
+
267
+ public func present<V: View>(
268
+ screenName: String,
269
+ @ViewBuilder _ content: @escaping () -> V,
270
+ options: NavigationOptions? = nil
271
+ ) {
272
+ let route = DynamicScreenRegistry.shared.register(
273
+ screenName: screenName,
274
+ content: { AnyView(content()) },
275
+ options: options
276
+ )
277
+ presented = DynamicDialogRoute(id: route.id)
278
+ }
279
+
280
+ public func dismiss() {
281
+ if let id = presented?.id {
282
+ DynamicScreenRegistry.shared.unregister(id: id)
283
+ }
284
+ presented = nil
285
+ }
286
+
287
+ // MARK: Overplay
288
+
289
+ public func showModal<V: View>(
290
+ @ViewBuilder _ content: @escaping () -> V,
291
+ barrierDismissible: Bool = true,
292
+ onDismiss: (() -> Void)? = nil
293
+ ) {
294
+ overplayDismissHandler = nil
295
+ overplay = OverplayItem(
296
+ type: .modal,
297
+ content: { AnyView(content()) },
298
+ barrierDismissible: barrierDismissible,
299
+ onDismiss: onDismiss
300
+ )
301
+ }
302
+
303
+ public func showBottomSheet<V: View>(
304
+ @ViewBuilder _ content: @escaping () -> V,
305
+ isSurface: Bool = false,
306
+ barrierDismissible: Bool = true,
307
+ header: BottomSheetHeader? = nil,
308
+ onDismiss: (() -> Void)? = nil
309
+ ) {
310
+ overplayDismissHandler = nil
311
+ overplay = OverplayItem(
312
+ type: .bottomSheet,
313
+ content: { AnyView(content()) },
314
+ barrierDismissible: barrierDismissible,
315
+ onDismiss: onDismiss,
316
+ isSurface: isSurface,
317
+ header: header
318
+ )
319
+ }
320
+
321
+ /// Mirrors Compose's `showSnackBar(snackBar:)`.
322
+ public func showSnackBar(_ snackBar: SnackBar, onDismiss: (() -> Void)? = nil) {
323
+ overplayDismissHandler = nil
324
+ overplay = OverplayItem(
325
+ type: .snackBar,
326
+ content: { AnyView(EmptyView()) },
327
+ barrierDismissible: false,
328
+ onDismiss: onDismiss,
329
+ snackBar: snackBar
330
+ )
331
+ }
332
+
333
+ // MARK: Overplay dismissal
334
+
335
+ /// Registered by the active overplay component so external dismiss requests
336
+ /// run its exit animation (mirrors Compose's `bindClose`).
337
+ public func registerOverplayDismiss(_ handler: @escaping () -> Void) {
338
+ overplayDismissHandler = handler
339
+ }
340
+
341
+ /// Removes the overplay and fires its `onDismiss` (mirrors Compose's
342
+ /// `hardClearAfterDismiss`). Called by a component once its exit animation
343
+ /// has completed.
344
+ public func finishOverplayDismiss() {
345
+ let dismissCallback = overplay?.onDismiss
346
+ overplayDismissHandler = nil
347
+ overplay = nil
348
+ dismissCallback?()
349
+ }
350
+
351
+ public func hideOverplay() {
352
+ if let close = overplayDismissHandler {
353
+ // Component runs its exit animation, then calls finishOverplayDismiss.
354
+ overplayDismissHandler = nil
355
+ close()
356
+ } else {
357
+ finishOverplayDismiss()
358
+ }
359
+ }
360
+
361
+ public func hideSnackBar() {
362
+ if overplay?.type == .snackBar { hideOverplay() }
363
+ }
364
+ }
@@ -0,0 +1,188 @@
1
+ //
2
+ // BottomSheet.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `BottomSheet` (navigation/BottomSheet.kt).
6
+ // A sheet that slides up from the bottom over a dimmed scrim, with a drag
7
+ // handle, a header (title or custom), a divider and the content body.
8
+ // Dragging down past a threshold (or tapping the scrim) dismisses it.
9
+ // Driven by `Navigator.showBottomSheet`.
10
+ //
11
+
12
+ import SwiftUI
13
+
14
+ // MARK: - Header model
15
+
16
+ /// Mirrors Compose's `sealed class BottomHeader` (Title / Custom).
17
+ public struct BottomSheetHeader {
18
+ enum Style {
19
+ case title(String)
20
+ case custom(() -> AnyView)
21
+ }
22
+
23
+ let style: Style
24
+
25
+ /// Mirrors `BottomHeader.Title`.
26
+ public static func title(_ text: String = "Bottom Sheet Title") -> BottomSheetHeader {
27
+ BottomSheetHeader(style: .title(text))
28
+ }
29
+
30
+ /// Mirrors `BottomHeader.Custom`.
31
+ public static func custom<V: View>(@ViewBuilder _ content: @escaping () -> V) -> BottomSheetHeader {
32
+ BottomSheetHeader(style: .custom({ AnyView(content()) }))
33
+ }
34
+ }
35
+
36
+ // MARK: - Bottom sheet view
37
+
38
+ @available(iOS 16.0, *)
39
+ struct BottomSheetView<Content: View>: View {
40
+ let header: BottomSheetHeader
41
+ let isSurface: Bool
42
+ let barrierDismissible: Bool
43
+ let content: () -> Content
44
+
45
+ @EnvironmentObject private var navigator: Navigator
46
+
47
+ /// Vertical translation of the sheet; starts off-screen and animates to 0.
48
+ @State private var sheetOffset: CGFloat = UIScreen.main.bounds.height
49
+ @State private var scrimAlpha: CGFloat = 0
50
+ @State private var sheetHeight: CGFloat = 0
51
+ @State private var didAppear = false
52
+
53
+ /// Mirrors Compose's `sheetCloseOffset` (100dp drag threshold to dismiss).
54
+ private let closeThreshold: CGFloat = 100
55
+
56
+ private var screenHeight: CGFloat { UIScreen.main.bounds.height }
57
+ private var bottomInset: CGFloat { safeAreaBottomInset() }
58
+
59
+ private var backgroundColor: Color {
60
+ // Compose uses background.surface (white) for `isSurface`, otherwise
61
+ // background.default. Both resolve to white in the current iOS palette.
62
+ isSurface ? Colors.black01 : Colors.black01
63
+ }
64
+
65
+ var body: some View {
66
+ ZStack(alignment: .bottom) {
67
+ // Dimmed scrim (max 0.3 alpha, mirrors Compose).
68
+ Color.black.opacity(scrimAlpha)
69
+ .ignoresSafeArea()
70
+ .contentShape(Rectangle())
71
+ .onTapGesture {
72
+ if barrierDismissible { close() }
73
+ }
74
+
75
+ sheet
76
+ .offset(y: sheetOffset)
77
+ }
78
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
79
+ .onAppear {
80
+ guard !didAppear else { return }
81
+ didAppear = true
82
+ navigator.registerOverplayDismiss { close() }
83
+ open()
84
+ }
85
+ }
86
+
87
+ private var sheet: some View {
88
+ VStack(spacing: 0) {
89
+ // Drag handle + header (the draggable region).
90
+ VStack(spacing: 0) {
91
+ Spacer().frame(height: 8)
92
+ Capsule()
93
+ .fill(Colors.black06)
94
+ .frame(width: 40, height: 4)
95
+ Spacer().frame(height: 4)
96
+ headerView
97
+ .frame(height: 56)
98
+ .padding(.horizontal, Spacing.M)
99
+ }
100
+ .frame(maxWidth: .infinity)
101
+ .frame(height: 72)
102
+ .contentShape(Rectangle())
103
+ .gesture(dragGesture)
104
+
105
+ Rectangle()
106
+ .fill(Colors.black04)
107
+ .frame(height: 1)
108
+
109
+ content()
110
+
111
+ backgroundColor
112
+ .frame(height: bottomInset)
113
+ }
114
+ .frame(maxWidth: .infinity)
115
+ .frame(maxHeight: screenHeight - safeAreaTopInset() - 90, alignment: .bottom)
116
+ .background(
117
+ GeometryReader { geo in
118
+ Color.clear.onAppear { sheetHeight = geo.size.height }
119
+ }
120
+ )
121
+ .background(backgroundColor)
122
+ .clipShape(RoundedCorner(radius: Radius.M, corners: [.topLeft, .topRight]))
123
+ }
124
+
125
+ @ViewBuilder
126
+ private var headerView: some View {
127
+ switch header.style {
128
+ case .title(let text):
129
+ HStack(spacing: 0) {
130
+ Color.clear.frame(width: 24, height: 24)
131
+ MomoText(text, typography: .headerDefaultBold, color: Colors.black17)
132
+ .frame(maxWidth: .infinity, alignment: .center)
133
+ Icon(source: "navigation_close", size: 24, color: Colors.black17)
134
+ .frame(width: 24, height: 24)
135
+ .contentShape(Rectangle())
136
+ .onTapGesture { close() }
137
+ }
138
+ case .custom(let builder):
139
+ builder()
140
+ }
141
+ }
142
+
143
+ // MARK: - Drag handling (mirrors Compose's detectDragGestures)
144
+
145
+ private var dragGesture: some Gesture {
146
+ DragGesture()
147
+ .onChanged { value in
148
+ let newOffset = max(value.translation.height, 0)
149
+ sheetOffset = newOffset
150
+ }
151
+ .onEnded { _ in
152
+ if sheetOffset > closeThreshold {
153
+ close()
154
+ } else {
155
+ withAnimation(.spring(response: 0.3, dampingFraction: 0.9)) {
156
+ sheetOffset = 0
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ // MARK: - Open / close animations
163
+
164
+ private func open() {
165
+ DispatchQueue.main.async {
166
+ withAnimation(.easeOut(duration: 0.1)) { scrimAlpha = 0.3 }
167
+ withAnimation(.timingCurve(0.05, 0.7, 0.1, 1, duration: 0.35)) { sheetOffset = 0 }
168
+ }
169
+ }
170
+
171
+ private func close() {
172
+ withAnimation(.timingCurve(0.3, 0, 0.8, 0.15, duration: 0.2)) {
173
+ sheetOffset = max(sheetHeight, screenHeight)
174
+ scrimAlpha = 0
175
+ }
176
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
177
+ navigator.finishOverplayDismiss()
178
+ }
179
+ }
180
+ }
181
+
182
+ @available(iOS 16.0, *)
183
+ private func safeAreaBottomInset() -> CGFloat {
184
+ let bottomInset = UIApplication.shared.connectedScenes
185
+ .compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
186
+ .first ?? 0
187
+ return min(bottomInset, 21)
188
+ }
@@ -0,0 +1,68 @@
1
+ //
2
+ // ModalScreen.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `ModalScreen` (navigation/ModalScreen.kt).
6
+ // A centered overlay that fades + scales in over a dimmed scrim, and animates
7
+ // back out before the overplay is removed. Driven by `Navigator.showModal`.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ @available(iOS 16.0, *)
13
+ struct ModalScreen<Content: View>: View {
14
+ let barrierDismissible: Bool
15
+ let content: () -> Content
16
+
17
+ @EnvironmentObject private var navigator: Navigator
18
+
19
+ @State private var scrimOpacity: CGFloat = 0
20
+ @State private var contentScale: CGFloat = 0.8
21
+ @State private var contentOpacity: CGFloat = 0
22
+ @State private var didAppear = false
23
+
24
+ var body: some View {
25
+ ZStack {
26
+ // Dimmed scrim (mirrors `Color.Black.copy(alpha = 0.6f * alpha)`).
27
+ Color.black.opacity(0.6 * scrimOpacity)
28
+ .ignoresSafeArea()
29
+ .contentShape(Rectangle())
30
+ .onTapGesture {
31
+ if barrierDismissible { navigator.hideOverplay() }
32
+ }
33
+
34
+ content()
35
+ .scaleEffect(contentScale)
36
+ .opacity(contentOpacity)
37
+ }
38
+ .onAppear {
39
+ guard !didAppear else { return }
40
+ didAppear = true
41
+ navigator.registerOverplayDismiss { close() }
42
+ open()
43
+ }
44
+ }
45
+
46
+ // Mirrors Compose's `openEvent`: 250ms fade + scale to 1.
47
+ private func open() {
48
+ DispatchQueue.main.async {
49
+ withAnimation(.easeOut(duration: 0.25)) {
50
+ scrimOpacity = 1
51
+ contentScale = 1
52
+ contentOpacity = 1
53
+ }
54
+ }
55
+ }
56
+
57
+ // Mirrors Compose's `closeEvent`: 200ms fade + scale back to 0.8, then remove.
58
+ private func close() {
59
+ withAnimation(.easeIn(duration: 0.2)) {
60
+ scrimOpacity = 0
61
+ contentScale = 0.8
62
+ contentOpacity = 0
63
+ }
64
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
65
+ navigator.finishOverplayDismiss()
66
+ }
67
+ }
68
+ }