@momo-kits/native-kits 0.163.1-beta.2 → 0.163.1-beta.20-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 (345) hide show
  1. package/.claude/settings.local.json +112 -0
  2. package/CLAUDE.md +1 -1
  3. package/build.gradle.kts +11 -0
  4. package/compose/build.gradle.kts +190 -0
  5. package/compose/build.gradle.kts.backup +190 -0
  6. package/compose/compose.podspec +47 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +150 -0
  10. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  11. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  23. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +106 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +112 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +477 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +214 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +276 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +52 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +38 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +39 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +299 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +100 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +172 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +372 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +581 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +156 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +69 -0
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  133. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
  134. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  135. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +207 -0
  136. package/gradle/libs.versions.toml +69 -0
  137. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  138. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  139. package/gradle.properties +26 -0
  140. package/gradlew +252 -0
  141. package/gradlew.bat +94 -0
  142. package/ios/Application/ApplicationEnvironment.swift +15 -3
  143. package/ios/Application/Components.swift +36 -30
  144. package/ios/Application/FloatingButton.swift +10 -9
  145. package/ios/Application/Localize.swift +309 -0
  146. package/ios/Application/MaxApi.swift +18 -0
  147. package/ios/Application/Navigation/BottomTab/BottomTab.swift +104 -0
  148. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  149. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  150. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  151. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  152. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  153. package/ios/Application/Navigation/NavigationContainer.swift +203 -0
  154. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  155. package/ios/Application/Navigation/Navigator.swift +385 -0
  156. package/ios/Application/Navigation/Overplay/BottomSheet.swift +256 -0
  157. package/ios/Application/Navigation/Overplay/ModalScreen.swift +74 -0
  158. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  159. package/ios/Application/Navigation/Tracking/ScreenTracker.swift +117 -0
  160. package/ios/Application/Navigation/component/Header.swift +178 -0
  161. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  162. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  163. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +11 -0
  164. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +196 -0
  165. package/ios/Application/Screen.swift +2 -1
  166. package/ios/Application/StackScreen.swift +501 -0
  167. package/ios/Avatar/Avatar.swift +193 -0
  168. package/ios/Badge/Badge.swift +11 -14
  169. package/ios/Badge/BadgeRibbon.swift +91 -17
  170. package/ios/BaselineView/BaselineView.swift +163 -0
  171. package/ios/Button/Button.swift +137 -28
  172. package/ios/Carousel/Carousel.swift +196 -0
  173. package/ios/Checkbox/Checkbox.swift +23 -28
  174. package/ios/Chip/Chip.swift +56 -33
  175. package/ios/Collapse/Collapse.swift +192 -0
  176. package/ios/Colors+Radius+Spacing/Colors.swift +20 -17
  177. package/ios/Colors+Radius+Spacing/Spacing.swift +3 -1
  178. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  179. package/ios/DateTimePicker/DateTimePicker.swift +212 -0
  180. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  181. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  182. package/ios/DateTimePicker/WheelPicker.swift +165 -0
  183. package/ios/Divider/Divider.swift +16 -0
  184. package/ios/Extensions/ActiveOpacityButtonStyle.swift +14 -0
  185. package/ios/Extensions/Color++.swift +32 -0
  186. package/ios/Icon/Icon.swift +10 -1
  187. package/ios/IconButton/IconButton.swift +140 -0
  188. package/ios/Image/Image.swift +16 -3
  189. package/ios/Information/Information.swift +3 -1
  190. package/ios/Input/ErrorView.swift +3 -1
  191. package/ios/Input/Input.swift +72 -33
  192. package/ios/Input/InputPhoneNumber.swift +33 -21
  193. package/ios/Input/InputSearch.swift +107 -48
  194. package/ios/Input/InputTextArea.swift +134 -64
  195. package/ios/InputDropDown/InputDropDown.swift +201 -0
  196. package/ios/InputMoney/InputMoney.swift +347 -0
  197. package/ios/InputOTP/InputOTP.swift +210 -0
  198. package/ios/Loader/Loader.swift +113 -0
  199. package/ios/MoMoUIKits.podspec +5 -0
  200. package/ios/Pagination/PaginationDot.swift +61 -0
  201. package/ios/Pagination/PaginationNumber.swift +35 -0
  202. package/ios/Pagination/PaginationScroll.swift +101 -0
  203. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  204. package/ios/Popup/PopupDisplay.swift +11 -20
  205. package/ios/Popup/PopupInput.swift +2 -8
  206. package/ios/Popup/PopupNotify.swift +218 -0
  207. package/ios/Popup/PopupPromotion.swift +5 -3
  208. package/ios/ProgressInfo/ProgressInfo.swift +296 -0
  209. package/ios/Radio/Radio.swift +69 -0
  210. package/ios/Rating/Rating.swift +82 -0
  211. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +11 -0
  212. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +38 -0
  213. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +38 -0
  214. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +38 -0
  215. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +6 -0
  216. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +38 -0
  217. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +38 -0
  218. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +38 -0
  219. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +38 -0
  220. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +38 -0
  221. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +38 -0
  222. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +38 -0
  223. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +38 -0
  224. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
  225. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
  226. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
  227. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
  228. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
  229. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
  230. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
  231. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
  232. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
  233. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
  234. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
  235. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
  236. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
  237. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
  238. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
  239. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
  240. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
  241. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
  242. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
  243. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +38 -0
  244. package/ios/Skeleton/Skeleton.swift +99 -0
  245. package/ios/Slider/Slider.swift +237 -0
  246. package/ios/Stepper/Stepper.swift +220 -0
  247. package/ios/Steps/Steps.swift +467 -0
  248. package/ios/SuggestAction/SuggestAction.swift +99 -0
  249. package/ios/Swipeable/SwipeCell.swift +187 -0
  250. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  251. package/ios/Switch/Switch.swift +53 -28
  252. package/ios/TabView/TabView.swift +534 -0
  253. package/ios/Tag/Tag.swift +98 -0
  254. package/ios/Template/TrustBanner/TrustBanner.swift +7 -8
  255. package/ios/Title/Title.swift +269 -0
  256. package/ios/Tooltip/Tooltip.swift +485 -0
  257. package/ios/Typography/FontScaleStore.swift +19 -0
  258. package/ios/Typography/Text.swift +86 -17
  259. package/ios/Typography/Typography.swift +12 -14
  260. package/ios/Uploader/Uploader.swift +174 -0
  261. package/ios/native-kits.podspec +61 -8
  262. package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
  263. package/ios-demo/README.md +120 -0
  264. package/ios-demo/Resources/dot_loading.json +558 -0
  265. package/ios-demo/Resources/icon.json +4052 -0
  266. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  267. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  268. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  269. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  270. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  271. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  272. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  273. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  274. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  275. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  276. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  277. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
  278. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  279. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  280. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  281. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  282. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  283. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  284. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  285. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +64 -0
  286. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  287. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  288. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  289. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  290. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  291. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  292. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +279 -0
  293. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  294. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  295. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  296. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  297. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  298. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  299. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  300. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  301. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  302. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  303. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  304. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +188 -0
  305. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +229 -0
  306. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  307. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  308. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  309. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  310. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  311. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  312. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  313. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  314. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  315. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  316. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  317. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  318. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  319. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  320. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  321. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  322. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  323. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  324. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  325. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  326. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  327. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  328. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +51 -0
  329. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  330. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  331. package/local.properties +8 -0
  332. package/package.json +1 -1
  333. package/publish.sh +53 -0
  334. package/scripts/gen-ios-color.mjs +136 -0
  335. package/settings.gradle.kts +64 -0
  336. package/example/ios/Example.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  337. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  338. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  339. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  340. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  341. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  342. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  343. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  344. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -46
  345. package/ios/Application/FontScaleStore.swift +0 -59
@@ -0,0 +1,201 @@
1
+ //
2
+ // InputDropDown.swift
3
+ // Pods
4
+ //
5
+ // Hand-written SwiftUI reimplementation of Compose `InputDropDown`.
6
+ //
7
+
8
+ import SwiftUI
9
+
10
+ // MARK: - RenderRightIcon (mirrors Compose RenderRightIcon for InputDropDown)
11
+
12
+ private struct RenderRightIcon: View {
13
+ let loading: Bool
14
+ let icon: String
15
+ let color: Color
16
+ let onPressed: () -> Void
17
+
18
+ var body: some View {
19
+ // Mirrors Compose's two independent `if` blocks: loading spinner and icon
20
+ // can both render at once, not exclusively (not an if/else).
21
+ if loading {
22
+ ActivityIndicator(isAnimating: .constant(true), style: .medium)
23
+ .frame(width: 16, height: 16)
24
+ }
25
+ if !icon.isEmpty {
26
+ SwiftUI.Button(action: onPressed) {
27
+ Icon(source: icon, size: 24, color: color)
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ // MARK: - InputDropDown
34
+
35
+ public struct InputDropDown: View {
36
+ @Environment(\.appTheme) private var theme
37
+ @Binding public var value: String
38
+
39
+ public var floatingValue: String
40
+ public var floatingValueColor: Color?
41
+ public var floatingIcon: String
42
+ public var floatingIconColor: Color?
43
+ public var placeholder: String
44
+ public var size: InputSize
45
+ public var onPress: () -> Void
46
+ public var hintText: String
47
+ public var error: String
48
+ public var errorSpacing: Bool
49
+ public var disabled: Bool
50
+ public var icon: String
51
+ public var iconColor: Color?
52
+ public var onRightIconPressed: () -> Void
53
+ public var leadingIcon: String
54
+ public var leadingIconColor: Color?
55
+ public var loading: Bool
56
+ public var required: Bool
57
+
58
+ public init(
59
+ value: Binding<String>,
60
+ floatingValue: String = "",
61
+ floatingValueColor: Color? = nil,
62
+ floatingIcon: String = "",
63
+ floatingIconColor: Color? = nil,
64
+ placeholder: String = "",
65
+ size: InputSize = .small,
66
+ onPress: @escaping () -> Void = {},
67
+ hintText: String = "",
68
+ error: String = "",
69
+ errorSpacing: Bool = false,
70
+ disabled: Bool = false,
71
+ icon: String = "arrow_chevron_down_small",
72
+ iconColor: Color? = nil,
73
+ onRightIconPressed: @escaping () -> Void = {},
74
+ leadingIcon: String = "",
75
+ leadingIconColor: Color? = nil,
76
+ loading: Bool = false,
77
+ required: Bool = false
78
+ ) {
79
+ self._value = value
80
+ self.floatingValue = floatingValue
81
+ self.floatingValueColor = floatingValueColor
82
+ self.floatingIcon = floatingIcon
83
+ self.floatingIconColor = floatingIconColor
84
+ self.placeholder = placeholder
85
+ self.size = size
86
+ self.onPress = onPress
87
+ self.hintText = hintText
88
+ self.error = error
89
+ self.errorSpacing = errorSpacing
90
+ self.disabled = disabled
91
+ self.icon = icon
92
+ self.iconColor = iconColor
93
+ self.onRightIconPressed = onRightIconPressed
94
+ self.leadingIcon = leadingIcon
95
+ self.leadingIconColor = leadingIconColor
96
+ self.loading = loading
97
+ self.required = required
98
+ }
99
+
100
+ public var body: some View {
101
+ VStack(alignment: .leading, spacing: 4) {
102
+ ZStack(alignment: .topLeading) {
103
+ // Floating label
104
+ if !floatingValue.isEmpty || !floatingIcon.isEmpty {
105
+ HStack(spacing: 0) {
106
+ MomoText(floatingValue, typography: .labelSMedium, color: floatingTitleColor)
107
+ if required {
108
+ MomoText("*", typography: .labelSMedium, color: theme.colors.error.primary)
109
+ }
110
+ if !floatingIcon.isEmpty {
111
+ Icon(source: floatingIcon, size: 16, color: floatingIconTintColor)
112
+ .padding(.leading, Spacing.XS)
113
+ }
114
+ }
115
+ .padding(.horizontal, Spacing.S)
116
+ .background(theme.colors.background.surface)
117
+ .offset(x: Spacing.S, y: -8)
118
+ .zIndex(10)
119
+ }
120
+
121
+ // Input container
122
+ HStack(alignment: .center, spacing: 0) {
123
+ if !leadingIcon.isEmpty {
124
+ Icon(source: leadingIcon, size: size == .small ? 24 : 32, color: leadingIconTintColor)
125
+ .padding(.trailing, Spacing.M)
126
+ }
127
+
128
+ MomoText(
129
+ value.isEmpty ? placeholder : value,
130
+ typography: .bodyDefaultRegular,
131
+ color: value.isEmpty ? placeholderColor : textColor
132
+ )
133
+ .frame(maxWidth: .infinity, alignment: .leading)
134
+
135
+ RenderRightIcon(
136
+ loading: loading,
137
+ icon: icon,
138
+ color: iconTintColor,
139
+ onPressed: onRightIconPressed
140
+ )
141
+ }
142
+ .padding(.horizontal, Spacing.M)
143
+ .frame(height: size == .small ? 48 : 56)
144
+ .overlay(
145
+ RoundedRectangle(cornerRadius: Radius.S)
146
+ .stroke(getBorderColor(isFocused: false, error: error, disabled: disabled), lineWidth: 1)
147
+ )
148
+ }
149
+ .contentShape(Rectangle())
150
+ .onTapGesture {
151
+ if !disabled { onPress() }
152
+ }
153
+
154
+ ErrorView(
155
+ errorMessage: error,
156
+ errorSpacing: errorSpacing,
157
+ hintText: hintText
158
+ )
159
+ }
160
+ .accessibilityElement(children: .combine)
161
+ .accessibility(label: Text(floatingValue))
162
+ }
163
+
164
+ // MARK: - Computed colors
165
+
166
+ private var textColor: Color {
167
+ disabled ? theme.colors.text.disable : theme.colors.text.default
168
+ }
169
+
170
+ private var placeholderColor: Color {
171
+ disabled ? theme.colors.text.disable : theme.colors.text.hint
172
+ }
173
+
174
+ private var iconTintColor: Color {
175
+ disabled ? theme.colors.text.disable : (iconColor ?? theme.colors.text.default)
176
+ }
177
+
178
+ private var floatingTitleColor: Color {
179
+ disabled ? theme.colors.text.disable : (floatingValueColor ?? theme.colors.text.hint)
180
+ }
181
+
182
+ private var floatingIconTintColor: Color {
183
+ disabled ? theme.colors.text.disable : (floatingIconColor ?? theme.colors.text.default)
184
+ }
185
+
186
+ private var leadingIconTintColor: Color {
187
+ disabled ? theme.colors.text.disable : (leadingIconColor ?? theme.colors.text.hint)
188
+ }
189
+
190
+ private func getBorderColor(isFocused: Bool, error: String, disabled: Bool) -> Color {
191
+ if disabled {
192
+ return theme.colors.border.disable
193
+ } else if !error.isEmpty {
194
+ return theme.colors.error.primary
195
+ } else if isFocused {
196
+ return theme.colors.primary
197
+ } else {
198
+ return theme.colors.border.default
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,347 @@
1
+ //
2
+ // InputMoney.swift
3
+ // MoMoUIKits
4
+ //
5
+ // Native SwiftUI reimplementation of Compose `InputMoney`.
6
+ // The bound `text` holds RAW DIGITS only (mirrors Compose, where the
7
+ // field stores the numeric string and a VisualTransformation renders the
8
+ // thousands-separated currency). Display formatting is applied live.
9
+ //
10
+
11
+ import Foundation
12
+ import SwiftUI
13
+ import UIKit
14
+
15
+ // MARK: - Currency formatting
16
+
17
+ /// Mirrors Compose `formatNumberToMoney` exactly (group separator ".").
18
+ /// Input is a raw-digit string; output has thousands separators + currency.
19
+ public func formatNumberToMoney(_ digits: String, currency: String = "đ") -> String {
20
+ let numeric = digits.filter { $0.isNumber }
21
+ if numeric.isEmpty || numeric == "0" {
22
+ return "0" + currency
23
+ }
24
+ // Strip leading zeros to match Long parsing semantics.
25
+ var trimmed = String(numeric.drop(while: { $0 == "0" }))
26
+ if trimmed.isEmpty { trimmed = "0" }
27
+ if trimmed == "0" {
28
+ return "0" + currency
29
+ }
30
+ if trimmed.count < 3 {
31
+ return trimmed + currency
32
+ }
33
+ var chars: [Character] = []
34
+ var count = 0
35
+ let reversed = Array(trimmed.reversed())
36
+ for i in 0..<reversed.count {
37
+ count += 1
38
+ chars.append(reversed[i])
39
+ // i counts from the least significant digit; original index = len-1-i.
40
+ let originalIndex = reversed.count - 1 - i
41
+ if count == 3 && originalIndex >= 1 {
42
+ chars.append(".")
43
+ count = 0
44
+ }
45
+ }
46
+ return String(chars.reversed()) + currency
47
+ }
48
+
49
+ // MARK: - InputMoney
50
+
51
+ public struct InputMoney: View {
52
+ @Environment(\.appTheme) private var theme
53
+ @Binding public var text: String
54
+
55
+ public var floatingValue: String
56
+ public var floatingValueColor: Color?
57
+ public var floatingIcon: String
58
+ public var floatingIconColor: Color?
59
+ public var placeholder: String
60
+ public var size: InputSize
61
+ public var error: String
62
+ public var errorSpacing: Bool
63
+ public var disabled: Bool
64
+ public var icon: String
65
+ public var iconColor: Color?
66
+ public var leadingIcon: String
67
+ public var leadingIconColor: Color?
68
+ public var loading: Bool
69
+ public var required: Bool
70
+ public var fontWeight: InputFontWeight
71
+ public var keyboardType: UIKeyboardType
72
+ public var onChangeText: ((String) -> Void)?
73
+ public var onPressIcon: (() -> Void)?
74
+ public var onFocus: (() -> Void)?
75
+ public var onBlur: (() -> Void)?
76
+
77
+ @State private var isFocused: Bool = false
78
+ @State private var floatingLabelHeight: CGFloat = 16
79
+
80
+ public init(
81
+ text: Binding<String>,
82
+ floatingValue: String = "",
83
+ floatingValueColor: Color? = nil,
84
+ floatingIcon: String = "",
85
+ floatingIconColor: Color? = nil,
86
+ placeholder: String = "0đ",
87
+ size: InputSize = .small,
88
+ error: String = "",
89
+ errorSpacing: Bool = false,
90
+ disabled: Bool = false,
91
+ icon: String = "",
92
+ iconColor: Color? = nil,
93
+ leadingIcon: String = "",
94
+ leadingIconColor: Color? = nil,
95
+ loading: Bool = false,
96
+ required: Bool = false,
97
+ fontWeight: InputFontWeight = .bold,
98
+ keyboardType: UIKeyboardType = .numberPad,
99
+ onChangeText: ((String) -> Void)? = nil,
100
+ onPressIcon: (() -> Void)? = nil,
101
+ onFocus: (() -> Void)? = nil,
102
+ onBlur: (() -> Void)? = nil
103
+ ) {
104
+ self._text = text
105
+ self.floatingValue = floatingValue
106
+ self.floatingValueColor = floatingValueColor
107
+ self.floatingIcon = floatingIcon
108
+ self.floatingIconColor = floatingIconColor
109
+ self.placeholder = placeholder
110
+ self.size = size
111
+ self.error = error
112
+ self.errorSpacing = errorSpacing
113
+ self.disabled = disabled
114
+ self.icon = icon
115
+ self.iconColor = iconColor
116
+ self.leadingIcon = leadingIcon
117
+ self.leadingIconColor = leadingIconColor
118
+ self.loading = loading
119
+ self.required = required
120
+ self.fontWeight = fontWeight
121
+ self.keyboardType = keyboardType
122
+ self.onChangeText = onChangeText
123
+ self.onPressIcon = onPressIcon
124
+ self.onFocus = onFocus
125
+ self.onBlur = onBlur
126
+ }
127
+
128
+ // MARK: - Body
129
+
130
+ public var body: some View {
131
+ VStack(alignment: .leading, spacing: 4) {
132
+ ZStack(alignment: .topLeading) {
133
+ // Floating label
134
+ if !floatingValue.isEmpty || !floatingIcon.isEmpty {
135
+ HStack(spacing: Spacing.XS) {
136
+ MomoText(floatingValue, typography: .labelSMedium, color: floatingTitleColor)
137
+ if required {
138
+ MomoText("*", typography: .labelSMedium, color: theme.colors.error.primary)
139
+ }
140
+ if !floatingIcon.isEmpty {
141
+ Icon(source: floatingIcon, size: 16, color: floatingIconTintColor)
142
+ }
143
+ }
144
+ .padding(.horizontal, Spacing.S)
145
+ .background(
146
+ GeometryReader { geo in
147
+ Color.clear.onAppear { floatingLabelHeight = geo.size.height }
148
+ }
149
+ )
150
+ .background(theme.colors.background.surface)
151
+ // Kotlin centers the label within the box-height-tall decorationBox by
152
+ // default, then applies `offsetY = -size.values.height / 2` — net effect:
153
+ // the label ends up centered exactly on the box's top border. Our ZStack
154
+ // anchors top-leading instead, so matching that requires offsetting by
155
+ // half the label's OWN measured height, not half the box height.
156
+ .offset(x: Spacing.S, y: -floatingLabelHeight / 2)
157
+ .zIndex(10)
158
+ }
159
+
160
+ // Input container
161
+ HStack(alignment: .center, spacing: 0) {
162
+ // Leading icon
163
+ if !leadingIcon.isEmpty {
164
+ Icon(source: leadingIcon, size: size == .small ? 24 : 32, color: leadingIconTintColor)
165
+ .padding(.trailing, Spacing.XS)
166
+ }
167
+
168
+ // Money field
169
+ ZStack(alignment: .leading) {
170
+ if text.filter({ $0.isNumber }).isEmpty {
171
+ MomoText(
172
+ placeholder,
173
+ typography: fontWeight == .bold ? .actionSBold : .bodyDefaultRegular,
174
+ color: placeholderColor
175
+ )
176
+ }
177
+ MoneyTextField(
178
+ digits: $text,
179
+ keyboardType: keyboardType,
180
+ fontWeight: fontWeight == .bold ? .bold : .regular,
181
+ textColor: UIColor(textColor),
182
+ cursorColor: UIColor(theme.colors.primary),
183
+ isDisabled: disabled,
184
+ onFocusChange: { focused in handleFocusChange(focused) },
185
+ onChangeText: onChangeText
186
+ )
187
+ }
188
+ .frame(maxWidth: .infinity, alignment: .leading)
189
+
190
+ // Clear button (focused + has value)
191
+ if isFocused && !text.filter({ $0.isNumber }).isEmpty {
192
+ SwiftUI.Button(action: {
193
+ text = ""
194
+ onChangeText?("")
195
+ }) {
196
+ Icon(source: "24_navigation_close_circle_full", size: 16, color: theme.colors.text.hint)
197
+ .padding(.leading, Spacing.XS)
198
+ .accessibility(identifier: "ic_clear")
199
+ }
200
+ }
201
+
202
+ // Loading indicator and right icon can both render (mirrors
203
+ // Compose's two independent `if` blocks, not if/else).
204
+ if loading {
205
+ ActivityIndicator(isAnimating: .constant(true), style: .medium)
206
+ .frame(width: 16, height: 16)
207
+ .padding(.leading, Spacing.S)
208
+ }
209
+ if !icon.isEmpty {
210
+ SwiftUI.Button(action: { onPressIcon?() }) {
211
+ Icon(source: icon, size: 24, color: iconTintColor)
212
+ .padding(.leading, Spacing.S)
213
+ }
214
+ }
215
+ }
216
+ .padding(.horizontal, Spacing.M)
217
+ .frame(height: scaleSize(size == .small ? 48 : 56, 1.1))
218
+ .background(
219
+ RoundedRectangle(cornerRadius: Radius.S)
220
+ .fill(theme.colors.background.surface)
221
+ )
222
+ .overlay(
223
+ RoundedRectangle(cornerRadius: Radius.S)
224
+ .stroke(borderColor, lineWidth: 1)
225
+ )
226
+ }
227
+
228
+ ErrorView(errorMessage: error, errorSpacing: errorSpacing, hintText: "")
229
+ }
230
+ .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
231
+ if isFocused {
232
+ DispatchQueue.main.async { handleFocusChange(false) }
233
+ }
234
+ }
235
+ }
236
+
237
+ // MARK: - Helpers
238
+
239
+ private func handleFocusChange(_ focused: Bool) {
240
+ guard isFocused != focused else { return }
241
+ isFocused = focused
242
+ if focused { onFocus?() } else { onBlur?() }
243
+ }
244
+
245
+ private var borderColor: Color {
246
+ if disabled { return theme.colors.border.disable }
247
+ if !error.isEmpty { return theme.colors.error.primary }
248
+ if isFocused { return theme.colors.primary }
249
+ return theme.colors.border.default
250
+ }
251
+
252
+ private var textColor: Color { disabled ? theme.colors.text.disable : theme.colors.text.default }
253
+ private var placeholderColor: Color { disabled ? theme.colors.text.disable : theme.colors.text.hint }
254
+ private var iconTintColor: Color { disabled ? theme.colors.text.disable : (iconColor ?? theme.colors.text.default) }
255
+ private var leadingIconTintColor: Color { disabled ? theme.colors.text.disable : (leadingIconColor ?? theme.colors.text.hint) }
256
+ private var floatingTitleColor: Color { disabled ? theme.colors.text.disable : (floatingValueColor ?? theme.colors.text.hint) }
257
+ private var floatingIconTintColor: Color { disabled ? theme.colors.text.disable : (floatingIconColor ?? theme.colors.text.default) }
258
+ }
259
+
260
+ // MARK: - MoneyTextField (UIViewRepresentable)
261
+
262
+ /// A UITextField that shows live thousands-separated currency while the bound
263
+ /// `digits` string holds only the raw numeric value. Caret is pinned before
264
+ /// the trailing currency symbol.
265
+ private struct MoneyTextField: UIViewRepresentable {
266
+ @Binding var digits: String
267
+ var keyboardType: UIKeyboardType
268
+ var fontWeight: UIFont.Weight
269
+ var textColor: UIColor
270
+ var cursorColor: UIColor
271
+ var isDisabled: Bool
272
+ var onFocusChange: (Bool) -> Void
273
+ var onChangeText: ((String) -> Void)?
274
+
275
+ func makeCoordinator() -> Coordinator { Coordinator(self) }
276
+
277
+ func makeUIView(context: Context) -> UITextField {
278
+ let field = UITextField()
279
+ field.delegate = context.coordinator
280
+ field.keyboardType = keyboardType
281
+ field.font = UIFont.systemFont(ofSize: scaleSize(20), weight: fontWeight)
282
+ field.textColor = textColor
283
+ field.isEnabled = !isDisabled
284
+ field.tintColor = cursorColor
285
+ field.setContentHuggingPriority(.defaultLow, for: .horizontal)
286
+ field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
287
+ field.text = displayValue(digits)
288
+ field.addTarget(
289
+ context.coordinator,
290
+ action: #selector(Coordinator.editingChanged(_:)),
291
+ for: .editingChanged
292
+ )
293
+ return field
294
+ }
295
+
296
+ func updateUIView(_ field: UITextField, context: Context) {
297
+ context.coordinator.parent = self
298
+ field.isEnabled = !isDisabled
299
+ field.textColor = textColor
300
+ field.tintColor = cursorColor
301
+ let expected = displayValue(digits)
302
+ if !field.isFirstResponder && field.text != expected {
303
+ field.text = expected
304
+ }
305
+ }
306
+
307
+ private func displayValue(_ raw: String) -> String {
308
+ let numeric = raw.filter { $0.isNumber }
309
+ return numeric.isEmpty ? "" : formatNumberToMoney(numeric)
310
+ }
311
+
312
+ final class Coordinator: NSObject, UITextFieldDelegate {
313
+ var parent: MoneyTextField
314
+
315
+ init(_ parent: MoneyTextField) { self.parent = parent }
316
+
317
+ func textFieldDidBeginEditing(_ textField: UITextField) {
318
+ parent.onFocusChange(true)
319
+ }
320
+
321
+ func textFieldDidEndEditing(_ textField: UITextField) {
322
+ parent.onFocusChange(false)
323
+ }
324
+
325
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
326
+ textField.resignFirstResponder()
327
+ return true
328
+ }
329
+
330
+ @objc func editingChanged(_ textField: UITextField) {
331
+ // Extract digits from whatever the user typed/deleted.
332
+ let numeric = (textField.text ?? "").filter { $0.isNumber }
333
+ let formatted = numeric.isEmpty ? "" : formatNumberToMoney(numeric)
334
+ if textField.text != formatted {
335
+ textField.text = formatted
336
+ // Keep caret before the currency suffix ("đ").
337
+ if let end = textField.position(from: textField.endOfDocument, offset: -1) {
338
+ textField.selectedTextRange = textField.textRange(from: end, to: end)
339
+ }
340
+ }
341
+ if parent.digits != numeric {
342
+ parent.digits = numeric
343
+ }
344
+ parent.onChangeText?(numeric)
345
+ }
346
+ }
347
+ }