@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
@@ -101,69 +101,98 @@ struct ToolkitHeaderRight: View {
101
101
  var tintColor: Color?
102
102
  @State private var isFavorite: Bool = false
103
103
  @State private var isLoading: Bool = false
104
- @EnvironmentObject private var environment: ApplicationEnvironment
104
+ @Environment(\.applicationEnvironment) private var environment
105
+
106
+ private func apiValue(_ value: Any?) -> Any {
107
+ value ?? NSNull()
108
+ }
109
+
110
+ private var contextMap: [String: Any] {
111
+ let context = environment.applicationContext
112
+ return [
113
+ "appId": apiValue(context?.appId),
114
+ "code": apiValue(context?.appCode),
115
+ "name": apiValue(context?.appName),
116
+ "icon": apiValue(context?.appIcon),
117
+ "description": apiValue(context?.description),
118
+ "support": apiValue(context?.support),
119
+ "toolkitConfig": apiValue(context?.toolkitConfig),
120
+ "providerId": apiValue(context?.providerId),
121
+ "permissions": apiValue(context?.permissions)
122
+ ]
123
+ }
124
+
125
+ private var shouldShowShortcut: Bool {
126
+ headerRight?.useShortcut == true && !(headerRight?.useMore == true && environment.applicationContext == nil)
127
+ }
128
+
129
+ private func parseResponse(_ response: String) -> Any? {
130
+ guard let jsonData = response.data(using: .utf8),
131
+ let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
132
+ return nil
133
+ }
134
+ return json["response"]
135
+ }
105
136
 
106
137
  // MARK: - Actions
138
+ private func loadFavoriteState() {
139
+ environment.composeApi?.request(
140
+ funcName: "isFavoriteApp",
141
+ params: ["code": apiValue(environment.applicationContext?.appCode)]
142
+ ) { response in
143
+ if let isFavorite = parseResponse(response) as? Bool {
144
+ self.isFavorite = isFavorite
145
+ }
146
+ }
147
+ }
148
+
107
149
  private func onPressShortcut() {
108
150
  environment.composeApi?.request(
109
151
  funcName: "onToolAction",
110
- params: ["item": ["key": "onFavorite"]]
111
- ) { _ in
112
- isFavorite.toggle()
152
+ params: [
153
+ "item": ["key": "onFavorite"],
154
+ "context": contextMap
155
+ ]
156
+ ) { response in
157
+ if let result = parseResponse(response) as? [String: Any],
158
+ result["success"] as? Bool == true {
159
+ isFavorite.toggle()
160
+ }
113
161
  }
114
162
  }
115
163
 
116
164
  private func onPressHelpCenter() {
117
- let context = environment.applicationContext
118
- let paramMap: [String: Any] = [
119
- "appId": context?.appId,
120
- "code": context?.appCode,
121
- "name": context?.appName,
122
- "icon": context?.appIcon,
123
- "description": context?.description
124
- ]
125
165
  environment.composeApi?.request(
126
166
  funcName: "showHelpCenter",
127
- params: paramMap
167
+ params: [
168
+ "appId": apiValue(environment.applicationContext?.appId),
169
+ "code": apiValue(environment.applicationContext?.appCode),
170
+ "name": apiValue(environment.applicationContext?.appName),
171
+ "icon": apiValue(environment.applicationContext?.appIcon),
172
+ "description": apiValue(environment.applicationContext?.description)
173
+ ]
128
174
  ) { _ in }
129
175
  }
130
176
 
131
177
  private func onPressClose() {
132
178
  environment.composeApi?.request(
133
179
  funcName: "dismissAll",
134
- params: ""
180
+ params: nil
135
181
  ) { _ in }
136
182
  }
137
183
 
138
184
  private func onPressMore() {
139
- let context = environment.applicationContext
140
185
  let params: [String: Any] = [
141
- "useSystemTools": headerRight?.useSystemTools,
186
+ "useSystemTools": headerRight?.useSystemTools ?? true,
142
187
  "tools": headerRight?.tools.map { $0.toMap() } ?? [],
143
- "context": [
144
- "appId": context?.appId,
145
- "code": context?.appCode,
146
- "name": context?.appName,
147
- "icon": context?.appIcon,
148
- "description": context?.description,
149
- "support": context?.support,
150
- "toolkitConfig": context?.toolkitConfig,
151
- "providerId": context?.providerId,
152
- "permissions": context?.permissions
153
- ]
188
+ "context": contextMap
154
189
  ]
155
190
  environment.composeApi?.request(
156
191
  funcName: "showTools",
157
192
  params: params
158
193
  ) { response in
159
- do {
160
- if let jsonData = response.data(using: .utf8),
161
- let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
162
- let toolResponse = json["response"] as? String {
163
- headerRight?.toolCallback?(toolResponse)
164
- }
165
- } catch {
166
- print("Error parsing response:", error)
194
+ if let toolResponse = parseResponse(response) as? String {
195
+ headerRight?.toolCallback?(toolResponse)
167
196
  }
168
197
  }
169
198
  }
@@ -195,7 +224,7 @@ struct ToolkitHeaderRight: View {
195
224
  } ?? false
196
225
 
197
226
  HStack(alignment: .center, spacing: 0) {
198
- if headerRight?.useShortcut == true {
227
+ if shouldShowShortcut {
199
228
  NavigationButton(
200
229
  disabled: isLoading,
201
230
  icon: navButtonConfig.icon,
@@ -206,15 +235,17 @@ struct ToolkitHeaderRight: View {
206
235
  }
207
236
 
208
237
  HStack(alignment: .center, spacing: 0) {
209
- Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
210
- .padding(4)
211
- .onTapGesture {
212
- onPressHelpCenter()
213
- }
214
-
215
- Rectangle()
216
- .fill(tintColor ?? Colors.black20)
217
- .frame(width: 0.5, height: 12)
238
+ if environment.applicationContext != nil {
239
+ Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
240
+ .padding(4)
241
+ .onTapGesture {
242
+ onPressHelpCenter()
243
+ }
244
+
245
+ Rectangle()
246
+ .fill(tintColor ?? Colors.black20)
247
+ .frame(width: 0.5, height: 12)
248
+ }
218
249
 
219
250
  Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
220
251
  .padding(4)
@@ -231,6 +262,9 @@ struct ToolkitHeaderRight: View {
231
262
  )
232
263
  .padding(.leading, Spacing.S)
233
264
  }
265
+ .onAppear {
266
+ loadFavoriteState()
267
+ }
234
268
  }
235
269
  }
236
270
 
@@ -0,0 +1,103 @@
1
+ //
2
+ // BottomTab.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `BottomTab` (navigation/bottomtab/BottomTab.kt).
6
+ // Hosts a set of tabbed screens, each wrapped in `StackScreen` chrome built
7
+ // from that tab's `NavigationOptions`, with a `BottomTabBar` pinned to the
8
+ // bottom. Switching tabs cross-fades + scales the content (parity with Compose).
9
+ //
10
+
11
+ import SwiftUI
12
+
13
+ @available(iOS 16.0, *)
14
+ public struct BottomTab: View {
15
+ private let items: [BottomTabItem]
16
+ private let floatingButton: BottomTabFloatingButton?
17
+
18
+ @EnvironmentObject private var navigator: Navigator
19
+ @State private var selectedIndex: Int = 0
20
+
21
+ public init(items: [BottomTabItem], floatingButton: BottomTabFloatingButton? = nil) {
22
+ self.items = items
23
+ self.floatingButton = floatingButton
24
+ }
25
+
26
+ private var bottomInset: CGFloat { safeAreaBottomInset() }
27
+ private var reservedBottom: CGFloat { BOTTOM_TAB_BAR_HEIGHT + bottomInset + Spacing.S }
28
+
29
+ public var body: some View {
30
+ ZStack(alignment: .bottom) {
31
+ // Tab content area, inset above the bar.
32
+ ZStack {
33
+ ForEach(items.indices, id: \.self) { index in
34
+ if index == selectedIndex {
35
+ tabScreen(for: index)
36
+ .transition(.scale(scale: 0.97).combined(with: .opacity))
37
+ }
38
+ }
39
+ }
40
+ .padding(.bottom, reservedBottom)
41
+ .animation(.easeInOut(duration: 0.2), value: selectedIndex)
42
+
43
+ // Tab bar + home-indicator filler.
44
+ VStack(spacing: 0) {
45
+ BottomTabBar(
46
+ items: items,
47
+ floatingButton: floatingButton,
48
+ selectedIndex: selectedIndex,
49
+ onTabSelected: { index in
50
+ if index != selectedIndex { selectedIndex = index }
51
+ }
52
+ )
53
+ Colors.black01
54
+ .frame(height: bottomInset + Spacing.S)
55
+ }
56
+ }
57
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
58
+ .ignoresSafeArea(edges: .bottom)
59
+ }
60
+
61
+ @ViewBuilder
62
+ private func tabScreen(for index: Int) -> some View {
63
+ let item = items[index]
64
+ let options = item.options ?? NavigationOptions()
65
+ StackScreen(
66
+ backgroundColor: options.backgroundColor,
67
+ headerTransparent: options.headerTransparent,
68
+ fullScreenContent: options.fullScreenContent,
69
+ tintColor: options.tintColor ?? Colors.black17,
70
+ isBack: !options.hiddenBack,
71
+ headerType: options.headerType,
72
+ useAnimated: options.useAnimated,
73
+ title: options.title,
74
+ headerTitle: options.headerTitle,
75
+ titlePosition: options.titlePosition,
76
+ // Tab back: non-first tab returns to the first tab, first tab
77
+ // delegates to the navigator (mirrors Compose's onBackHandler).
78
+ goBack: options.hiddenBack ? nil : {
79
+ if index != 0 { selectedIndex = 0 } else { navigator.onBackSafe() }
80
+ },
81
+ scrollable: options.scrollable,
82
+ scrollToTopCallback: options.scrollToTopCallback,
83
+ useAvoidKeyboard: options.useAvoidKeyboard,
84
+ keyboardShouldPersistTaps: options.keyboardShouldPersistTaps,
85
+ footer: options.footer,
86
+ headerRight: options.resolvedHeaderRight,
87
+ animatedHeader: options.animatedHeader,
88
+ layoutOffset: options.layoutOffset,
89
+ inputSearchProps: options.inputSearchProps,
90
+ fabProps: options.fabProps,
91
+ content: { item.screen() }
92
+ )
93
+ }
94
+ }
95
+
96
+ /// Bottom safe-area inset (home indicator).
97
+ @available(iOS 16.0, *)
98
+ private func safeAreaBottomInset() -> CGFloat {
99
+ let bottomInset = UIApplication.shared.connectedScenes
100
+ .compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
101
+ .first ?? 0
102
+ return min(bottomInset, 21)
103
+ }
@@ -0,0 +1,215 @@
1
+ //
2
+ // BottomTabBar.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `BottomTabBar` (navigation/bottomtab/BottomTabBar.kt).
6
+ // Renders the row of tab items, an animated selection indicator, a top shadow,
7
+ // badges/dots, and an optional center floating button (with the curved notch).
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ // MARK: - Models
13
+
14
+ /// Mirrors Compose's `BottomTabItem`.
15
+ public struct BottomTabItem: Identifiable {
16
+ public let id = UUID()
17
+ public let name: String
18
+ public let label: String
19
+ public let icon: String
20
+ public let showDot: Bool
21
+ public let badgeLabel: String?
22
+ public let options: NavigationOptions?
23
+ public let screen: () -> AnyView
24
+
25
+ public init(
26
+ name: String,
27
+ label: String,
28
+ icon: String,
29
+ showDot: Bool = false,
30
+ badgeLabel: String? = nil,
31
+ options: NavigationOptions? = nil,
32
+ @ViewBuilder screen: @escaping () -> some View
33
+ ) {
34
+ self.name = name
35
+ self.label = label
36
+ self.icon = icon
37
+ self.showDot = showDot
38
+ self.badgeLabel = badgeLabel
39
+ self.options = options
40
+ self.screen = { AnyView(screen()) }
41
+ }
42
+ }
43
+
44
+ /// Mirrors Compose's `BottomTabFloatingButton`.
45
+ public struct BottomTabFloatingButton {
46
+ public let icon: String
47
+ public let label: String
48
+ public let onPress: () -> Void
49
+
50
+ public init(icon: String, label: String, onPress: @escaping () -> Void) {
51
+ self.icon = icon
52
+ self.label = label
53
+ self.onPress = onPress
54
+ }
55
+ }
56
+
57
+ let bottomTabFloatingButtonWidth: CGFloat = 75
58
+ let BOTTOM_TAB_BAR_HEIGHT: CGFloat = 64
59
+
60
+ // MARK: - Tab bar
61
+
62
+ struct BottomTabBar: View {
63
+ let items: [BottomTabItem]
64
+ let floatingButton: BottomTabFloatingButton?
65
+ let selectedIndex: Int
66
+ let onTabSelected: (Int) -> Void
67
+
68
+ private var screenWidth: CGFloat { UIScreen.main.bounds.width }
69
+ private var mid: Int { items.count / 2 }
70
+
71
+ private var tabWidth: CGFloat {
72
+ if floatingButton != nil {
73
+ return (screenWidth - bottomTabFloatingButtonWidth) / CGFloat(items.count)
74
+ }
75
+ return screenWidth / CGFloat(items.count)
76
+ }
77
+
78
+ private var indicatorOffset: CGFloat {
79
+ let adjust: CGFloat = (floatingButton != nil && selectedIndex >= mid) ? bottomTabFloatingButtonWidth : 0
80
+ return tabWidth * CGFloat(selectedIndex) + tabWidth / 2 - screenWidth / 2 + adjust
81
+ }
82
+
83
+ var body: some View {
84
+ ZStack(alignment: .bottom) {
85
+ HStack(spacing: 0) {
86
+ if floatingButton == nil {
87
+ tabItems(from: 0, to: items.count)
88
+ } else {
89
+ tabItems(from: 0, to: mid)
90
+ Spacer()
91
+ .frame(width: bottomTabFloatingButtonWidth)
92
+ .padding(.horizontal, Spacing.XXS)
93
+ tabItems(from: mid, to: items.count)
94
+ }
95
+ }
96
+ .frame(maxWidth: .infinity)
97
+ .frame(height: BOTTOM_TAB_BAR_HEIGHT)
98
+ .background(Colors.black01)
99
+
100
+ // Animated selection indicator.
101
+ RoundedRectangle(cornerRadius: 2)
102
+ .fill(Colors.primary)
103
+ .frame(width: 44, height: 2)
104
+ .offset(x: indicatorOffset, y: -BOTTOM_TAB_BAR_HEIGHT + 2)
105
+ .animation(.easeInOut(duration: 0.25), value: selectedIndex)
106
+
107
+ // Top shadow.
108
+ LinearGradient(
109
+ colors: [Color.black.opacity(0.05), .clear],
110
+ startPoint: .bottom,
111
+ endPoint: .top
112
+ )
113
+ .frame(height: 6)
114
+ .frame(maxWidth: .infinity)
115
+ .offset(y: -BOTTOM_TAB_BAR_HEIGHT)
116
+
117
+ if let floatingButton {
118
+ CenterFloatingButton(data: floatingButton)
119
+ }
120
+ }
121
+ }
122
+
123
+ @ViewBuilder
124
+ private func tabItems(from: Int, to: Int) -> some View {
125
+ ForEach(from..<to, id: \.self) { index in
126
+ TabBarItem(
127
+ item: items[index],
128
+ selected: index == selectedIndex,
129
+ onClick: { onTabSelected(index) }
130
+ )
131
+ .frame(maxWidth: .infinity)
132
+ }
133
+ }
134
+ }
135
+
136
+ // MARK: - Tab item
137
+
138
+ private struct TabBarItem: View {
139
+ let item: BottomTabItem
140
+ let selected: Bool
141
+ let onClick: () -> Void
142
+
143
+ private var tintColor: Color { selected ? Colors.primary : Colors.black10 }
144
+
145
+ /// Mirrors Compose's `formatLabel`: hide a badge that is the number "0".
146
+ private var badgeLabel: String? {
147
+ guard let label = item.badgeLabel else { return nil }
148
+ if let n = Int(label), n == 0 { return nil }
149
+ return label
150
+ }
151
+
152
+ var body: some View {
153
+ ZStack(alignment: .topTrailing) {
154
+ VStack(spacing: 0) {
155
+ Icon(source: item.icon, size: 28, color: tintColor)
156
+ .padding(Spacing.XS)
157
+ MomoText(
158
+ item.label,
159
+ typography: selected ? .labelXsMedium : .descriptionXsRegular,
160
+ color: tintColor,
161
+ textAlign: .center,
162
+ maxLines: 1
163
+ )
164
+ .truncationMode(.tail)
165
+ }
166
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
167
+ .padding(.horizontal, Spacing.S)
168
+ .padding(.vertical, Spacing.S)
169
+
170
+ if let badgeLabel {
171
+ if badgeLabel.isEmpty {
172
+ BadgeDot(size: .small)
173
+ .offset(x: -Spacing.S, y: Spacing.XS)
174
+ } else {
175
+ Badge(label: badgeLabel)
176
+ .offset(x: -Spacing.XS, y: 0)
177
+ }
178
+ } else if item.showDot {
179
+ BadgeDot(size: .small)
180
+ .offset(x: -Spacing.S, y: Spacing.XS)
181
+ }
182
+ }
183
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
184
+ .padding(.horizontal, Spacing.XXS)
185
+ .contentShape(Rectangle())
186
+ .onTapGesture { onClick() }
187
+ }
188
+ }
189
+
190
+ // MARK: - Center floating button
191
+
192
+ private struct CenterFloatingButton: View {
193
+ let data: BottomTabFloatingButton
194
+
195
+ var body: some View {
196
+ VStack(spacing: 0) {
197
+ CurvedContainer()
198
+ Circle()
199
+ .fill(Colors.primary)
200
+ .frame(width: 48, height: 48)
201
+ .overlay(Icon(source: data.icon, size: 28, color: .white))
202
+ Spacer().frame(height: 4)
203
+ MomoText(data.label, typography: .labelXsMedium, color: Colors.black01, textAlign: .center, maxLines: 1)
204
+ .frame(maxWidth: .infinity)
205
+ .frame(height: 14)
206
+ .background(Colors.primary)
207
+ .clipShape(RoundedRectangle(cornerRadius: Radius.XL))
208
+ }
209
+ .frame(width: bottomTabFloatingButtonWidth)
210
+ .padding(.horizontal, Spacing.XXS)
211
+ .padding(.vertical, Spacing.S)
212
+ .contentShape(Rectangle())
213
+ .onTapGesture { data.onPress() }
214
+ }
215
+ }
@@ -0,0 +1,65 @@
1
+ //
2
+ // CurvedContainer.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `CurvedContainer` (navigation/bottomtab/CurvedContainer.kt).
6
+ // Draws the surface-colored notch that sits behind the center floating button,
7
+ // carving a circular cut-out into the tab bar's top edge.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ struct CurvedContainer: View {
13
+ var circleSize: CGFloat = 48
14
+ var tabBarItemIconSize: CGFloat = 28
15
+ var circleOverSize: CGFloat = 2
16
+ var width: CGFloat = 71
17
+ var height: CGFloat = 16
18
+ var color: Color = Colors.black01
19
+
20
+ var body: some View {
21
+ let circlePositionTop = circleSize - tabBarItemIconSize - 10
22
+ let circleOverTop = circlePositionTop + circleOverSize
23
+ let circleR = circleSize / 2
24
+ let circleOverR = circleR + circleOverSize
25
+ let centerX = width / 2
26
+
27
+ CurvedShape(circleOverTop: circleOverTop, centerX: centerX, circleOverR: circleOverR, width: width)
28
+ .fill(color)
29
+ .frame(width: width, height: height)
30
+ .offset(y: 24)
31
+ .zIndex(-1)
32
+ }
33
+ }
34
+
35
+ /// Mirrors Compose's `getPath`: a flat bar with a smooth circular dip in the
36
+ /// middle, sized to cradle the floating button.
37
+ private struct CurvedShape: Shape {
38
+ let circleOverTop: CGFloat
39
+ let centerX: CGFloat
40
+ let circleOverR: CGFloat
41
+ let width: CGFloat
42
+
43
+ func path(in rect: CGRect) -> Path {
44
+ var path = Path()
45
+ path.move(to: CGPoint(x: 0, y: circleOverTop))
46
+ path.addLine(to: CGPoint(x: centerX - circleOverR - 40, y: circleOverTop))
47
+
48
+ path.addCurve(
49
+ to: CGPoint(x: centerX, y: 0),
50
+ control1: CGPoint(x: centerX - circleOverR, y: circleOverTop),
51
+ control2: CGPoint(x: centerX - circleOverR, y: 0)
52
+ )
53
+ path.addCurve(
54
+ to: CGPoint(x: centerX + circleOverR + 40, y: circleOverTop),
55
+ control1: CGPoint(x: centerX + circleOverR, y: 0),
56
+ control2: CGPoint(x: centerX + circleOverR, y: circleOverTop)
57
+ )
58
+
59
+ path.addLine(to: CGPoint(x: width, y: circleOverTop))
60
+ path.addLine(to: CGPoint(x: width, y: 60))
61
+ path.addLine(to: CGPoint(x: 0, y: 60))
62
+ path.closeSubpath()
63
+ return path
64
+ }
65
+ }
@@ -0,0 +1,66 @@
1
+ //
2
+ // HeaderBackProps.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `HeaderBackProps` + `PopupNotifyProps`/`PopupAction`.
6
+ // When `preventBack` is set, `Navigator.onBackSafe` shows a confirm popup
7
+ // before actually leaving the screen — same behavior as Compose.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ /// Mirrors Compose's `PopupAction` (a titled button with an optional handler).
13
+ public struct PopupAction {
14
+ public let title: String
15
+ public let onPress: (() -> Void)?
16
+
17
+ public init(title: String, onPress: (() -> Void)? = nil) {
18
+ self.title = title
19
+ self.onPress = onPress
20
+ }
21
+ }
22
+
23
+ /// Mirrors Compose's `PopupNotifyProps` as consumed by `headerBackProps.preventBack`.
24
+ public struct PreventBackProps {
25
+ public var image: String
26
+ public var title: String
27
+ public var description: String
28
+ public var error: String
29
+ public var primary: PopupAction?
30
+ public var secondary: PopupAction?
31
+ public var buttonDirection: PopupButtonDirection
32
+ public var isShowCloseIcon: Bool
33
+ public var onIconClose: (() -> Void)?
34
+
35
+ public init(
36
+ image: String = "",
37
+ title: String = "",
38
+ description: String = "",
39
+ error: String = "",
40
+ primary: PopupAction? = nil,
41
+ secondary: PopupAction? = nil,
42
+ buttonDirection: PopupButtonDirection = .row,
43
+ isShowCloseIcon: Bool = true,
44
+ onIconClose: (() -> Void)? = nil
45
+ ) {
46
+ self.image = image
47
+ self.title = title
48
+ self.description = description
49
+ self.error = error
50
+ self.primary = primary
51
+ self.secondary = secondary
52
+ self.buttonDirection = buttonDirection
53
+ self.isShowCloseIcon = isShowCloseIcon
54
+ self.onIconClose = onIconClose
55
+ }
56
+ }
57
+
58
+ /// Mirrors Compose's `HeaderBackProps`.
59
+ public struct HeaderBackProps {
60
+ /// When non-nil, a confirm popup is shown before the back navigation runs.
61
+ public var preventBack: PreventBackProps?
62
+
63
+ public init(preventBack: PreventBackProps? = nil) {
64
+ self.preventBack = preventBack
65
+ }
66
+ }
@@ -0,0 +1,57 @@
1
+ //
2
+ // HeaderTitle.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `sealed class HeaderTitle` (navigation/component/Header.kt).
6
+ // The header can show either a plain title (`.default`) or a rich "user" title
7
+ // with an avatar group, subtitle, inline icons and a status dot (`.user`).
8
+ // (Compose's `Location`/`Journey` variants are not rendered yet, so they are
9
+ // omitted here for parity with the implemented `HeaderUser`.)
10
+ //
11
+
12
+ import SwiftUI
13
+
14
+ /// Data backing a `.user` header title (mirrors `HeaderTitle.User`).
15
+ public struct HeaderUserData {
16
+ public var title: String
17
+ public var subTitle: String?
18
+ public var image: [String]?
19
+ public var dotColor: Color?
20
+ public var tintColor: Color?
21
+ public var onPress: (() -> Void)?
22
+ public var icons: [String]
23
+ public var isLoading: Bool
24
+
25
+ public init(
26
+ title: String,
27
+ subTitle: String? = nil,
28
+ image: [String]? = nil,
29
+ dotColor: Color? = nil,
30
+ tintColor: Color? = nil,
31
+ onPress: (() -> Void)? = nil,
32
+ icons: [String] = [],
33
+ isLoading: Bool = false
34
+ ) {
35
+ self.title = title
36
+ self.subTitle = subTitle
37
+ self.image = image
38
+ self.dotColor = dotColor
39
+ self.tintColor = tintColor
40
+ self.onPress = onPress
41
+ self.icons = icons
42
+ self.isLoading = isLoading
43
+ }
44
+ }
45
+
46
+ /// Mirrors Compose's `HeaderTitle` sealed class.
47
+ public enum HeaderTitle {
48
+ case `default`(String)
49
+ case user(HeaderUserData)
50
+
51
+ /// Plain title text, when this is a `.default` title (used by the legacy
52
+ /// string-based `Header`/`NavigationOptions` paths).
53
+ public var defaultTitle: String? {
54
+ if case let .default(text) = self { return text }
55
+ return nil
56
+ }
57
+ }