@momo-kits/native-kits 0.300.3 → 0.300.4-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 (272) hide show
  1. package/CLAUDE.md +51 -7
  2. package/README.md +2 -2
  3. package/build.gradle.kts +13 -0
  4. package/compose/build.gradle.kts +197 -0
  5. package/compose/build.gradle.kts.backup +197 -0
  6. package/compose/compose.podspec +47 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +20 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
  10. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  11. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +153 -0
  12. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +110 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +1071 -0
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +65 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +81 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +115 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +86 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +478 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +265 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +265 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +361 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +108 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +308 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +330 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +207 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +92 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +560 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +501 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +236 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +180 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +17 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +194 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +287 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +54 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +50 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +306 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +41 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +338 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +137 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +116 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +215 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +404 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +732 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +40 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +233 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +217 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +97 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +377 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +455 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +103 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +74 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +413 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +39 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +363 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HelpCenterRequest.kt +37 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +192 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +133 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +41 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +135 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +74 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +20 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +118 -0
  124. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/HelpCenterRequestTest.kt +44 -0
  125. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +62 -0
  126. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +42 -0
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +36 -0
  129. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
  130. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  131. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +217 -0
  132. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +138 -0
  133. package/gradle/libs.versions.toml +68 -0
  134. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  135. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  136. package/gradle.properties +26 -0
  137. package/gradlew +252 -0
  138. package/gradlew.bat +94 -0
  139. package/ios/Application/ApplicationEnvironment.swift +24 -6
  140. package/ios/Application/DesignSystemKits.swift +100 -0
  141. package/ios/Application/DesignSystemKitsPayload.swift +68 -0
  142. package/ios/Application/MaxApi.swift +30 -3
  143. package/ios/Avatar/Avatar.swift +4 -3
  144. package/ios/Badge/Badge.swift +6 -4
  145. package/ios/Badge/BadgeDot.swift +1 -1
  146. package/ios/Badge/BadgeRibbon.swift +8 -2
  147. package/ios/Button/Button.swift +15 -12
  148. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
  149. package/ios/Carousel/Carousel.swift +29 -12
  150. package/ios/Checkbox/Checkbox.swift +7 -3
  151. package/ios/Chip/Chip.swift +5 -4
  152. package/ios/Collapse/Collapse.swift +70 -19
  153. package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
  154. package/ios/Colors+Radius+Spacing/Radius.swift +6 -0
  155. package/ios/DateTimePicker/DateTimePicker.swift +54 -20
  156. package/ios/DateTimePicker/WheelPicker.swift +103 -105
  157. package/ios/Divider/Divider.swift +37 -4
  158. package/ios/Extensions/Color++.swift +38 -9
  159. package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
  160. package/ios/Extensions/SkeletonUICompat.swift +11 -0
  161. package/ios/Icon/Icon.swift +21 -23
  162. package/ios/Icon/Icons.swift +1329 -0
  163. package/ios/IconButton/IconButton.swift +15 -7
  164. package/ios/Image/Image.swift +7 -23
  165. package/ios/Information/Information.swift +27 -9
  166. package/ios/Input/ErrorView.swift +2 -2
  167. package/ios/Input/Input.swift +171 -148
  168. package/ios/Input/InputPhoneNumber.swift +10 -9
  169. package/ios/Input/InputSearch.swift +107 -78
  170. package/ios/Input/InputTextArea.swift +133 -148
  171. package/ios/InputDropDown/InputDropDown.swift +10 -7
  172. package/ios/InputMoney/InputMoney.swift +32 -11
  173. package/ios/InputOTP/InputOTP.swift +22 -12
  174. package/ios/Layout/Card.swift +50 -0
  175. package/ios/Layout/GridContext.swift +187 -3
  176. package/ios/Layout/Item.swift +5 -4
  177. package/ios/Layout/ScreenLayout.swift +415 -0
  178. package/ios/Layout/Section.swift +4 -100
  179. package/ios/Loader/Loader.swift +5 -9
  180. package/ios/Lottie/LottieView.swift +14 -2
  181. package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +169 -29
  182. package/ios/Navigation/BottomTab/BottomTab.swift +332 -0
  183. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +18 -7
  184. package/ios/Navigation/BottomTab/DuoBarCompression.swift +104 -0
  185. package/ios/Navigation/BottomTab/DuoTabBar.swift +329 -0
  186. package/ios/{Application → Navigation/Component}/FloatingButton.swift +43 -54
  187. package/ios/Navigation/Component/Header.swift +545 -0
  188. package/ios/Navigation/Component/HeaderBackground.swift +154 -0
  189. package/ios/Navigation/Component/HeaderJourney.swift +73 -0
  190. package/ios/Navigation/Component/HeaderLocation.swift +48 -0
  191. package/ios/Navigation/Component/HeaderRight.swift +425 -0
  192. package/ios/Navigation/Component/HeaderTitle.swift +36 -0
  193. package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
  194. package/ios/Navigation/Component/SideBar.swift +183 -0
  195. package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +8 -8
  196. package/ios/Navigation/HostBackGestureBridge.swift +78 -0
  197. package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +9 -2
  198. package/ios/Navigation/Navigation.swift +204 -0
  199. package/ios/Navigation/NavigationContainer.swift +195 -0
  200. package/ios/{Application/Navigation → Navigation}/Navigator.swift +207 -107
  201. package/ios/Navigation/StackScreen.swift +930 -0
  202. package/ios/Navigation/StackTransition.swift +400 -0
  203. package/ios/Navigation/SwipeBackHaptic.swift +33 -0
  204. package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
  205. package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
  206. package/ios/Pagination/PaginationScroll.swift +2 -2
  207. package/ios/Popup/PopupDisplay.swift +72 -135
  208. package/ios/Popup/PopupInput.swift +10 -13
  209. package/ios/Popup/PopupLayout.swift +138 -0
  210. package/ios/Popup/PopupNotify.swift +59 -69
  211. package/ios/Popup/PopupPromotion.swift +31 -47
  212. package/ios/PopupView/FullscreenPopup.swift +13 -19
  213. package/ios/PopupView/PopupView.swift +1 -1
  214. package/ios/PopupView/Utils++.swift +0 -8
  215. package/ios/ProgressInfo/ProgressInfo.swift +4 -2
  216. package/ios/Radio/Radio.swift +2 -1
  217. package/ios/Rating/Rating.swift +9 -1
  218. package/ios/Resources/dot_loading.json +558 -0
  219. package/ios/Resources/lottie_circle_loader.json +1 -0
  220. package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
  221. package/ios/Skeleton/Skeleton.swift +22 -27
  222. package/ios/Slider/Slider.swift +16 -18
  223. package/ios/Stepper/Stepper.swift +7 -3
  224. package/ios/Steps/Steps.swift +8 -6
  225. package/ios/SuggestAction/SuggestAction.swift +5 -0
  226. package/ios/Swipeable/SwipeCell.swift +10 -6
  227. package/ios/Switch/Switch.swift +22 -20
  228. package/ios/TabView/TabView.swift +102 -21
  229. package/ios/Tag/Tag.swift +15 -4
  230. package/ios/Template/Logo/Logo.swift +2 -1
  231. package/ios/Template/TrustBanner/TrustBanner.swift +109 -55
  232. package/ios/Title/Title.swift +6 -17
  233. package/ios/Tooltip/Tooltip.swift +29 -14
  234. package/ios/Typography/FontScale.swift +17 -14
  235. package/ios/Typography/Text.swift +156 -20
  236. package/ios/Typography/Typography.swift +2 -2
  237. package/ios/Uploader/Uploader.swift +3 -1
  238. package/ios/native-kits.podspec +2 -3
  239. package/ios-demo/MoMoUIKitsDemo.podspec +1 -3
  240. package/ios-demo/README.md +120 -0
  241. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
  242. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
  243. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +2 -0
  244. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
  245. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
  246. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +1 -0
  247. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +2 -2
  248. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +2 -0
  249. package/package.json +1 -1
  250. package/publish.sh +19 -2
  251. package/scripts/gen-ios-icons.mjs +36 -0
  252. package/settings.gradle.kts +64 -0
  253. package/ios/Application/Components.swift +0 -270
  254. package/ios/Application/ComposeApi.swift +0 -22
  255. package/ios/Application/HeaderRight.swift +0 -306
  256. package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
  257. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  258. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  259. package/ios/Application/Navigation/NavigationContainer.swift +0 -213
  260. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  261. package/ios/Application/Navigation/NavigatorManager.swift +0 -22
  262. package/ios/Application/Navigation/component/Header.swift +0 -178
  263. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
  264. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  265. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
  266. package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
  267. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
  268. package/ios/Application/Screen.swift +0 -250
  269. package/ios/Application/StackScreen.swift +0 -492
  270. package/ios/Typography/FontScaleStore.swift +0 -58
  271. package/ios/Typography/KitDesignSystem.swift +0 -17
  272. /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
@@ -11,15 +11,15 @@ import SDWebImageSwiftUI
11
11
 
12
12
  let jsonUrl = "https://static.momocdn.net/app/json/component-kits/design_system.json"
13
13
 
14
- public struct TrustBannerData: Decodable {
15
- let content: [String: String]
16
- let subContent: [String: String]
17
- let pciImage: String
18
- let sslImage: String
19
- let urlConfig: String
20
- let icons: [String]
21
- let momoImage: String
22
-
14
+ public struct TrustBannerData: Decodable, Equatable {
15
+ public let content: [String: String]
16
+ public let subContent: [String: String]
17
+ public let pciImage: String?
18
+ public let momoImage: String?
19
+ public let sslImage: String?
20
+ public let icons: [String]?
21
+ public let urlConfig: String
22
+
23
23
  enum CodingKeys: String, CodingKey {
24
24
  case content
25
25
  case subContent
@@ -29,6 +29,38 @@ public struct TrustBannerData: Decodable {
29
29
  case momoImage
30
30
  case urlConfig = "url_config"
31
31
  }
32
+
33
+ public init(
34
+ content: [String: String] = ["vi": "", "en": ""],
35
+ subContent: [String: String] = ["vi": "", "en": ""],
36
+ pciImage: String? = nil,
37
+ momoImage: String? = nil,
38
+ sslImage: String? = nil,
39
+ icons: [String]? = nil,
40
+ urlConfig: String = ""
41
+ ) {
42
+ self.content = content
43
+ self.subContent = subContent
44
+ self.pciImage = pciImage
45
+ self.momoImage = momoImage
46
+ self.sslImage = sslImage
47
+ self.icons = icons
48
+ self.urlConfig = urlConfig
49
+ }
50
+
51
+ // Missing keys fall back to the init defaults, like the Compose data class.
52
+ public init(from decoder: Decoder) throws {
53
+ let container = try decoder.container(keyedBy: CodingKeys.self)
54
+ self.init(
55
+ content: try container.decodeIfPresent([String: String].self, forKey: .content) ?? ["vi": "", "en": ""],
56
+ subContent: try container.decodeIfPresent([String: String].self, forKey: .subContent) ?? ["vi": "", "en": ""],
57
+ pciImage: try container.decodeIfPresent(String.self, forKey: .pciImage),
58
+ momoImage: try container.decodeIfPresent(String.self, forKey: .momoImage),
59
+ sslImage: try container.decodeIfPresent(String.self, forKey: .sslImage),
60
+ icons: try container.decodeIfPresent([String].self, forKey: .icons),
61
+ urlConfig: try container.decodeIfPresent(String.self, forKey: .urlConfig) ?? ""
62
+ )
63
+ }
32
64
  }
33
65
 
34
66
  let defaultBanner = TrustBannerData(
@@ -38,83 +70,105 @@ let defaultBanner = TrustBannerData(
38
70
  "vi": "Tìm hiểu thêm",
39
71
  "en": "Learn more"
40
72
  ],
41
- pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci.png",
73
+ pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci_dss.png",
74
+ momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png",
42
75
  sslImage: "https://static.momocdn.net/app/img/kits/trustBanner/ssl.png",
43
- urlConfig: "login_and_security",
44
76
  icons: [
45
77
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_viettinbank.png",
46
78
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_agribank.png",
47
79
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_vietcombank.png",
48
80
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_bidv.png"
49
81
  ],
50
- momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png"
82
+ urlConfig: "login_and_security"
51
83
  )
52
84
 
53
85
  public struct TrustBanner: View {
54
86
  @Environment(\.applicationEnvironment) var applicationEnvironment
55
87
  @Environment(\.localize) private var localize
88
+ @Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
56
89
 
57
90
  var onPress: ((String)->Void)?
91
+ private let serviceName: String
92
+ private let screenName: String
93
+ private let trackEvent: ((String, [String: String]) -> Void)?
58
94
 
59
- public init(onPress: ((String)->Void)?) {
95
+ public init(
96
+ onPress: ((String)->Void)?,
97
+ serviceName: String = "",
98
+ screenName: String = "",
99
+ trackEvent: ((String, [String: String]) -> Void)? = nil
100
+ ) {
60
101
  self.onPress = onPress
102
+ self.serviceName = serviceName
103
+ self.screenName = screenName
104
+ self.trackEvent = trackEvent
105
+ }
106
+
107
+ private var banner: TrustBannerData {
108
+ // Container config wins, else the host-pushed DesignSystemKits config (Compose parity).
109
+ (applicationEnvironment.config ?? designSystemKitsConfigs.designSystemConfig)?.trustBanner ?? defaultBanner
61
110
  }
62
111
 
63
112
  func onPressSecurity() {
64
- if (onPress != nil) {
65
- onPress!(applicationEnvironment.config?.trustBanner?.urlConfig ?? defaultBanner.urlConfig)
66
- }
113
+ trackEvent?("service_component_clicked", [
114
+ "service_name": serviceName,
115
+ "screen_name": screenName,
116
+ "component_name": "logo_trust",
117
+ "url_config": banner.urlConfig
118
+ ])
119
+ onPress?(banner.urlConfig)
67
120
  }
68
-
69
-
121
+
70
122
  public var body: some View {
71
- HStack(alignment: .center, spacing: 8) {
72
- WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.momoImage ?? defaultBanner.momoImage))
73
- .resizable()
74
- .scaledToFit()
75
- .frame(width: 64, height: 64)
76
- VStack(alignment: .leading){
77
- Text(applicationEnvironment.config?.trustBanner?.content[localize.currentLanguage]
78
- ?? localize.translateData(defaultBanner.content))
79
- .foregroundColor(Color(hex: "484848"))
80
- .lineLimit(2)
81
- .font(.system(size: 13, weight: Font.Weight.regular))
82
- .ignoreBoldTextSetting()
83
- .lineSpacing(3)
84
- .padding(.bottom, 8)
85
- HStack {
123
+ HStack(alignment: .top, spacing: 10) {
124
+ if let momoImage = banner.momoImage {
125
+ WebImage(url: URL(string: momoImage))
126
+ .resizable()
127
+ .scaledToFit()
128
+ .frame(width: 64, height: 64)
129
+ }
130
+ VStack(alignment: .leading, spacing: 0) {
131
+ MomoText(
132
+ localize.translateData(banner.content),
133
+ typography: .descriptionDefaultRegular,
134
+ color: Color(hex: "484848"),
135
+ maxLines: 2
136
+ )
137
+ .padding(.bottom, 8)
138
+ HStack(alignment: .top, spacing: 0) {
86
139
  HStack(spacing: 0) {
87
- Text(applicationEnvironment.config?.trustBanner?.subContent[localize.currentLanguage]
88
- ?? localize.translateData(defaultBanner.subContent))
89
- .foregroundColor(Colors.pink03)
90
- .font(.system(size: 13, weight: .bold))
91
- .ignoreBoldTextSetting()
92
- .padding(.trailing, 4)
93
-
140
+ MomoText(
141
+ localize.translateData(banner.subContent),
142
+ typography: .actionXsBold,
143
+ color: Colors.pink03,
144
+ maxLines: 2
145
+ )
94
146
  Icon(source: "arrow_chevron_right_small", color: Colors.pink03)
95
- }.onTapGesture {
96
- onPressSecurity()
97
147
  }
98
-
99
- Spacer()
100
- HStack{
101
- WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.pciImage ?? defaultBanner.pciImage))
102
- .resizable()
103
- .scaledToFit()
104
- .frame(width: 24, height: 20)
105
- WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.sslImage ?? defaultBanner.sslImage))
106
- .resizable()
107
- .scaledToFit()
108
- .frame(width: 52, height: 20)
148
+
149
+ Spacer(minLength: 0)
150
+ HStack(spacing: 0) {
151
+ if let pciImage = banner.pciImage {
152
+ WebImage(url: URL(string: pciImage))
153
+ .resizable()
154
+ .scaledToFit()
155
+ .frame(width: 27, height: 20)
156
+ .padding(.trailing, 4)
157
+ }
158
+ if let sslImage = banner.sslImage {
159
+ WebImage(url: URL(string: sslImage))
160
+ .resizable()
161
+ .scaledToFit()
162
+ .frame(width: 52, height: 20)
163
+ }
109
164
  }
110
165
  }
111
-
112
166
  }
113
-
114
167
  }
115
168
  .padding(.all, 12)
116
169
  .background(Color(hex: "F2F8FF"))
117
170
  .cornerRadius(12)
171
+ .contentShape(Rectangle())
118
172
  .onTapGesture {
119
173
  onPressSecurity()
120
174
  }
@@ -28,7 +28,6 @@ public enum TitleButtonSize {
28
28
 
29
29
  public struct Title: View {
30
30
  @Environment(\.appTheme) private var theme
31
- @Environment(\.momoFontScale) private var fontScale
32
31
 
33
32
  private let title: String
34
33
  private let type: TitleType
@@ -116,15 +115,9 @@ public struct Title: View {
116
115
  }
117
116
  }
118
117
 
119
- private var titleFontWeight: Font.Weight {
120
- switch type {
121
- case .card: return .regular
122
- case .section: return .bold
123
- }
124
- }
125
-
126
118
  private var titleMaxLines: Int {
127
- (showTrailingAction || badgeLabel != nil) ? 1 : 2
119
+ if textOnly { return Int.max }
120
+ return (showTrailingAction || badgeLabel != nil) ? 1 : 2
128
121
  }
129
122
 
130
123
  private var iconVerticalAlignment: VerticalAlignment {
@@ -151,14 +144,10 @@ public struct Title: View {
151
144
  }
152
145
  }
153
146
 
147
+ // Every Title variant is bold in the theme font; `headerDefaultBold` only supplies the weight.
154
148
  private var titleText: some View {
155
- SwiftUI.Text(title)
156
- .font(.system(size: fontScale(titleFontSize), weight: titleFontWeight))
157
- .ignoreBoldTextSetting()
158
- .foregroundColor(theme.colors.text.default)
159
- .lineSpacing(fontScale(titleLineHeight) - fontScale(titleFontSize))
160
- .lineLimit(titleMaxLines)
161
- .truncationMode(.tail)
149
+ MomoText(title, typography: .headerDefaultBold, maxLines: titleMaxLines)
150
+ .fontSize(titleFontSize, lineHeight: titleLineHeight)
162
151
  }
163
152
 
164
153
  private var fullLayout: some View {
@@ -237,7 +226,7 @@ public struct Title: View {
237
226
  }
238
227
  }
239
228
  .frame(maxWidth: .infinity, alignment: .leading)
240
- .padding(8)
229
+ .padding(.top, type == .section ? Spacing.S : 0)
241
230
  }
242
231
  }
243
232
 
@@ -3,7 +3,7 @@ import SwiftUI
3
3
  // MARK: - Types
4
4
 
5
5
  /// Tooltip placement relative to the anchor element.
6
- public enum TooltipPlacement {
6
+ public enum TooltipPlacement: Sendable {
7
7
  case top
8
8
  case bottom
9
9
  case left
@@ -78,24 +78,37 @@ struct TooltipArrowShape: Shape {
78
78
  func path(in rect: CGRect) -> Path {
79
79
  let w = rect.width
80
80
  let h = rect.height
81
- var path = Path()
81
+ let points: [CGPoint]
82
82
  switch placement {
83
83
  case .top:
84
- path.move(to: CGPoint(x: 0, y: 0))
85
- path.addLine(to: CGPoint(x: w, y: 0))
86
- path.addLine(to: CGPoint(x: w / 2, y: h))
84
+ points = [CGPoint(x: 0, y: 0), CGPoint(x: w, y: 0), CGPoint(x: w / 2, y: h)]
87
85
  case .bottom:
88
- path.move(to: CGPoint(x: 0, y: h))
89
- path.addLine(to: CGPoint(x: w, y: h))
90
- path.addLine(to: CGPoint(x: w / 2, y: 0))
86
+ points = [CGPoint(x: 0, y: h), CGPoint(x: w, y: h), CGPoint(x: w / 2, y: 0)]
91
87
  case .left:
92
- path.move(to: CGPoint(x: 0, y: 0))
93
- path.addLine(to: CGPoint(x: 0, y: h))
94
- path.addLine(to: CGPoint(x: w, y: h / 2))
88
+ points = [CGPoint(x: 0, y: 0), CGPoint(x: 0, y: h), CGPoint(x: w, y: h / 2)]
95
89
  case .right:
96
- path.move(to: CGPoint(x: w, y: 0))
97
- path.addLine(to: CGPoint(x: w, y: h))
98
- path.addLine(to: CGPoint(x: 0, y: h / 2))
90
+ points = [CGPoint(x: w, y: 0), CGPoint(x: w, y: h), CGPoint(x: 0, y: h / 2)]
91
+ }
92
+ // rounded like Compose's cornerPathEffect: each corner is cut `radius` along both edges
93
+ let radius = TooltipConst.arrowCornerRadius
94
+ func toward(_ from: CGPoint, _ to: CGPoint) -> CGPoint {
95
+ let dx = to.x - from.x
96
+ let dy = to.y - from.y
97
+ let length = (dx * dx + dy * dy).squareRoot()
98
+ guard length > 0 else { return from }
99
+ let distance = min(radius, length / 2)
100
+ return CGPoint(x: from.x + dx / length * distance, y: from.y + dy / length * distance)
101
+ }
102
+ var path = Path()
103
+ for index in 0..<points.count {
104
+ let corner = points[index]
105
+ let start = toward(corner, points[(index + points.count - 1) % points.count])
106
+ if index == 0 {
107
+ path.move(to: start)
108
+ } else {
109
+ path.addLine(to: start)
110
+ }
111
+ path.addQuadCurve(to: toward(corner, points[(index + 1) % points.count]), control: corner)
99
112
  }
100
113
  path.closeSubpath()
101
114
  return path
@@ -209,6 +222,8 @@ private struct TooltipPositionModifier: ViewModifier {
209
222
  }
210
223
  )
211
224
  .offset(x: originX, y: originY)
225
+ // the first pass has no size yet and would flash at the wrong spot
226
+ .opacity(popupSize == .zero ? 0 : 1)
212
227
  }
213
228
 
214
229
  private var originX: CGFloat {
@@ -30,9 +30,9 @@ public struct FontScale: Equatable {
30
30
  public func callAsFunction(_ size: CGFloat) -> CGFloat {
31
31
  guard useOSScaleRate else { return size * userScaleRate }
32
32
 
33
- // Device width is read live rather than snapshotted, matching Compose's
34
- // getScreenDimensions() read inside scaleSize(), so a rotation or split-view resize counts.
35
- let deviceScale = UIScreen.main.bounds.width / Self.defaultScreenSize
33
+ // Short side, not width: landscape must not inflate every font (and so every popup's height).
34
+ let bounds = UIScreen.main.bounds.size
35
+ let deviceScale = min(bounds.width, bounds.height) / Self.defaultScreenSize
36
36
 
37
37
  var deviceScaled = size
38
38
  var osScaled = size
@@ -52,7 +52,7 @@ public struct FontScale: Equatable {
52
52
  }
53
53
 
54
54
  /// A host-pushed mini-app context wins per field, matching Compose's
55
- /// `context?.useOSScaleRate ?: KitFontScale.useOSScaleRate`.
55
+ /// `context?.useOSScaleRate ?: DesignSystemKits.getConfigs().useOSScaleRate`.
56
56
  public func overriding(userScaleRate: CGFloat?, useOSScaleRate: Bool?) -> FontScale {
57
57
  guard userScaleRate != nil || useOSScaleRate != nil else { return self }
58
58
  return FontScale(
@@ -66,7 +66,7 @@ public struct FontScale: Equatable {
66
66
  private struct FontScaleKey: EnvironmentKey {
67
67
  /// Computed, never stored: a bare `UIHostingController` root that never got `.momoFontScale()`
68
68
  /// still reads the live store instead of a value frozen at first access.
69
- static var defaultValue: FontScale { FontScaleStore.shared.snapshot }
69
+ static var defaultValue: FontScale { DesignSystemKits.shared.fontScale }
70
70
  }
71
71
 
72
72
  public extension EnvironmentValues {
@@ -77,9 +77,10 @@ public extension EnvironmentValues {
77
77
  }
78
78
 
79
79
  public extension View {
80
- /// Injects a Dynamic-Type-observing font scale and re-renders this subtree on change.
80
+ /// Injects a Dynamic-Type-observing font scale plus `\.designSystemKitsConfigs` (whitelist, remote config)
81
+ /// and re-renders this subtree on change.
81
82
  /// Use at every `UIHostingController` root that is not inside a `NavigationContainer`.
82
- func momoFontScale(_ store: FontScaleStore = .shared, context: MiniAppContext? = nil) -> some View {
83
+ func momoFontScale(_ store: DesignSystemKits = .shared, context: MiniAppContext? = nil) -> some View {
83
84
  modifier(MomoFontScaleModifier(store: store, context: context))
84
85
  }
85
86
  }
@@ -87,17 +88,19 @@ public extension View {
87
88
  /// `@ObservedObject` is load-bearing, same as `MomoLocalizeModifier`: `@Environment(\.momoFontScale)`
88
89
  /// only invalidates when the boxed snapshot changes, which requires *this* body to re-run on publish.
89
90
  private struct MomoFontScaleModifier: ViewModifier {
90
- @ObservedObject var store: FontScaleStore
91
+ @ObservedObject var store: DesignSystemKits
91
92
  let context: MiniAppContext?
92
93
 
93
94
  func body(content: Content) -> some View {
94
- content.environment(
95
- \.momoFontScale,
96
- store.snapshot.overriding(
97
- userScaleRate: context?.userScaleRate,
98
- useOSScaleRate: context?.useOSScaleRate
95
+ content
96
+ .environment(
97
+ \.momoFontScale,
98
+ store.fontScale.overriding(
99
+ userScaleRate: context?.userScaleRate,
100
+ useOSScaleRate: context?.useOSScaleRate
101
+ )
99
102
  )
100
- )
103
+ .environment(\.designSystemKitsConfigs, store.getConfigs())
101
104
  }
102
105
  }
103
106
  // AI-GENERATED END: reactive font-scale snapshot injected through the environment
@@ -4,7 +4,7 @@ import SwiftUI
4
4
  /// free functions). Inside a SwiftUI view prefer `@Environment(\.momoFontScale)` so the view
5
5
  /// re-renders when Dynamic Type or the host preference changes.
6
6
  public func scaleSize(_ size: CGFloat) -> CGFloat {
7
- FontScaleStore.shared.snapshot(size)
7
+ DesignSystemKits.shared.fontScale(size)
8
8
  }
9
9
 
10
10
  @available(*, deprecated, message: "Max font scale is fixed at 1.5; this override is no longer applied. Drop the second argument.")
@@ -99,33 +99,130 @@ public extension TypographyStyle {
99
99
  }
100
100
  }
101
101
 
102
- private func momoFontName(family: String, weight: Font.Weight) -> String? {
103
- // Ngoai whitelist thi tra nil de momoFont() roi ve .system(...), dung nhu truoc DS-791.
104
- guard KitDesignSystem.shared.isWhiteList else { return nil }
102
+ private enum MomoFontFace {
103
+ case system
104
+ case named(String)
105
+ }
106
+
107
+ /// Port of Compose `getFontFamily`; SF Pro Text is the iOS system font, so the legacy family is `.system`.
108
+ private func momoFontFace(family: String, weight: Font.Weight, isWhiteList: Bool) -> MomoFontFace? {
109
+ let isNewFamily = family == "MomoTrustSans" || family == headerFontFamily
110
+ // Outside the whitelist the new DS families drop to the legacy font, as before DS-791.
111
+ if family == "SFProText" || (isNewFamily && !isWhiteList) { return .system }
112
+ // single-file families (Compose maps every weight to the same .otf)
113
+ if family == headerFontFamily || family == "MoMoSignature" { return .named(family) }
105
114
  guard family == "MomoTrustSans" else { return nil }
106
115
  switch weight {
107
- case .ultraLight, .thin: return "MomoTrustSans-ExtraLight"
108
- case .light: return "MomoTrustSans-Light"
109
- case .regular: return "MomoTrustSans-Regular"
110
- case .medium: return "MomoTrustSans-Medium"
111
- case .semibold: return "MomoTrustSans-SemiBold"
112
- case .bold: return "MomoTrustSans-Bold"
113
- case .heavy, .black: return "MomoTrustSans-ExtraBold"
114
- default: return "MomoTrustSans-Regular"
116
+ case .ultraLight, .thin: return .named("MomoTrustSans-ExtraLight")
117
+ case .light: return .named("MomoTrustSans-Light")
118
+ case .regular: return .named("MomoTrustSans-Regular")
119
+ case .medium: return .named("MomoTrustSans-Medium")
120
+ case .semibold: return .named("MomoTrustSans-SemiBold")
121
+ case .bold: return .named("MomoTrustSans-Bold")
122
+ case .heavy, .black: return .named("MomoTrustSans-ExtraBold")
123
+ default: return .named("MomoTrustSans-Regular")
115
124
  }
116
125
  }
117
126
 
118
- private func momoFont(size: CGFloat, weight: Font.Weight, family: String) -> Font {
119
- guard let name = momoFontName(family: family, weight: weight),
120
- UIFont(name: name, size: size) != nil else {
127
+ /// Header title family (Compose `HEADER_FONT_FAMILY`), whitelist only like the default font.
128
+ let headerFontFamily = "MoMoTrustDisplay"
129
+
130
+ private func momoFont(
131
+ size: CGFloat,
132
+ weight: Font.Weight,
133
+ family: String,
134
+ fallbackFamily: String? = nil,
135
+ isWhiteList: Bool
136
+ ) -> Font {
137
+ switch momoFontFace(family: family, weight: weight, isWhiteList: isWhiteList) {
138
+ case .system:
139
+ return .system(size: size, weight: weight)
140
+ case .named(let name) where UIFont(name: name, size: size) != nil:
141
+ return .custom(name, fixedSize: size)
142
+ default:
143
+ // Compose falls back to the default font file when a family is missing
144
+ if let fallbackFamily, fallbackFamily != family {
145
+ return momoFont(size: size, weight: weight, family: fallbackFamily, isWhiteList: isWhiteList)
146
+ }
121
147
  return .system(size: size, weight: weight)
122
148
  }
123
- return .custom(name, fixedSize: size)
149
+ }
150
+
151
+ // lineHeight is identical across weights of a family, so the regular face is enough.
152
+ private func momoLineHeight(size: CGFloat, family: String, fallbackFamily: String? = nil, isWhiteList: Bool) -> CGFloat {
153
+ switch momoFontFace(family: family, weight: .regular, isWhiteList: isWhiteList) {
154
+ case .system:
155
+ break
156
+ case .named(let name):
157
+ if let font = UIFont(name: name, size: size) { return font.lineHeight }
158
+ fallthrough
159
+ case nil:
160
+ if let fallbackFamily, fallbackFamily != family {
161
+ return momoLineHeight(size: size, family: fallbackFamily, isWhiteList: isWhiteList)
162
+ }
163
+ }
164
+ return UIFont.systemFont(ofSize: size).lineHeight
165
+ }
166
+
167
+ /// UIKit counterpart of `momoFont`, for text that is not a `MomoText` (UITextField/UITextView, text measuring).
168
+ func momoUIFont(size: CGFloat, weight: UIFont.Weight, family: String, isWhiteList: Bool) -> UIFont {
169
+ guard case .named(let name) = momoFontFace(family: family, weight: Font.Weight(weight), isWhiteList: isWhiteList),
170
+ let font = UIFont(name: name, size: size) else {
171
+ return .systemFont(ofSize: size, weight: weight)
172
+ }
173
+ return font
174
+ }
175
+
176
+ private extension Font.Weight {
177
+ init(_ weight: UIFont.Weight) {
178
+ switch weight {
179
+ case .ultraLight: self = .ultraLight
180
+ case .thin: self = .thin
181
+ case .light: self = .light
182
+ case .medium: self = .medium
183
+ case .semibold: self = .semibold
184
+ case .bold: self = .bold
185
+ case .heavy: self = .heavy
186
+ case .black: self = .black
187
+ default: self = .regular
188
+ }
189
+ }
190
+ }
191
+
192
+ /// Gives input text the same family as `MomoText` (Compose's `getFontFamily`), e.g. a TextField
193
+ /// next to a `MomoText` placeholder. `size` is unscaled; the environment font scale is applied here.
194
+ private struct KitFontModifier: ViewModifier {
195
+ @Environment(\.appTheme) private var theme
196
+ @Environment(\.momoFontScale) private var fontScale
197
+ @Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
198
+ let size: CGFloat
199
+ let weight: Font.Weight
200
+
201
+ func body(content: Content) -> some View {
202
+ content.font(momoFont(
203
+ size: fontScale(size),
204
+ weight: weight,
205
+ family: theme.font,
206
+ isWhiteList: designSystemKitsConfigs.isWhiteList
207
+ ))
208
+ }
209
+ }
210
+
211
+ extension View {
212
+ func kitFont(_ typography: TypographyStyle) -> some View {
213
+ modifier(KitFontModifier(size: typography.fontSize, weight: typography.fontWeight))
214
+ }
215
+
216
+ func kitFont(size: CGFloat, weight: Font.Weight = .regular) -> some View {
217
+ modifier(KitFontModifier(size: size, weight: weight))
218
+ }
124
219
  }
125
220
 
126
221
  public struct MomoText: View {
127
222
  @Environment(\.appTheme) private var theme
128
223
  @Environment(\.momoFontScale) private var fontScale
224
+ // From the environment, not DesignSystemKits.shared, so a whitelist flip re-renders the text.
225
+ @Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
129
226
  private let content: String
130
227
  private let typography: TypographyStyle
131
228
  private let color: Color?
@@ -139,7 +236,12 @@ public struct MomoText: View {
139
236
  private let letterSpacing: CGFloat?
140
237
  private let softWrap: Bool
141
238
  private let accessibilityId: String?
239
+ private let fontFamily: String?
142
240
  private var highlightText: [String] = []
241
+ private var sizeOverride: (fontSize: CGFloat, lineHeight: CGFloat)?
242
+
243
+ private var fontSize: CGFloat { sizeOverride?.fontSize ?? typography.fontSize }
244
+ private var lineHeight: CGFloat { sizeOverride?.lineHeight ?? typography.lineHeight }
143
245
 
144
246
  public init(
145
247
  _ content: String,
@@ -153,7 +255,8 @@ public struct MomoText: View {
153
255
  underline: Bool = false,
154
256
  letterSpacing: CGFloat? = nil,
155
257
  softWrap: Bool = true,
156
- accessibilityId: String? = nil
258
+ accessibilityId: String? = nil,
259
+ fontFamily: String? = nil
157
260
  ) {
158
261
  self.content = content
159
262
  self.typography = typography
@@ -175,6 +278,7 @@ public struct MomoText: View {
175
278
  self.letterSpacing = letterSpacing
176
279
  self.softWrap = softWrap
177
280
  self.accessibilityId = accessibilityId
281
+ self.fontFamily = fontFamily
178
282
  }
179
283
 
180
284
  init(_ content: String, typography: TypographyStyle, highlightText: [String]) {
@@ -182,11 +286,34 @@ public struct MomoText: View {
182
286
  self.highlightText = highlightText
183
287
  }
184
288
 
289
+ /// Compose's `style.copy(fontSize, lineHeight)`; the weight still comes from `typography`.
290
+ func fontSize(_ fontSize: CGFloat, lineHeight: CGFloat) -> MomoText {
291
+ var text = self
292
+ text.sizeOverride = (fontSize, lineHeight)
293
+ return text
294
+ }
295
+
296
+ private var font: Font {
297
+ momoFont(
298
+ size: fontScale(fontSize),
299
+ weight: typography.fontWeight,
300
+ family: fontFamily ?? theme.font,
301
+ fallbackFamily: theme.font,
302
+ isWhiteList: designSystemKitsConfigs.isWhiteList
303
+ )
304
+ }
305
+
185
306
  // Whole-word matches of `highlightText` render semibold; the longest keyword wins (RN parity).
186
307
  private var baseText: SwiftUI.Text {
187
308
  let keywords = highlightText.filter { !$0.isEmpty }.sorted { $0.count > $1.count }
188
309
  guard !keywords.isEmpty else { return SwiftUI.Text(content) }
189
- let highlightFont = momoFont(size: fontScale(typography.fontSize), weight: TypographyStyle.headerXsSemibold.fontWeight, family: theme.font)
310
+ let highlightFont = momoFont(
311
+ size: fontScale(fontSize),
312
+ weight: TypographyStyle.headerXsSemibold.fontWeight,
313
+ family: fontFamily ?? theme.font,
314
+ fallbackFamily: theme.font,
315
+ isWhiteList: designSystemKitsConfigs.isWhiteList
316
+ )
190
317
  func isWordChar(_ character: Character?) -> Bool {
191
318
  guard let character else { return false }
192
319
  return character.isLetter || character.isNumber || character == "_"
@@ -222,8 +349,17 @@ public struct MomoText: View {
222
349
  // *Strikethrough styles) or the explicit `strikethrough` flag.
223
350
  let isStrikethrough = strikethrough || typography.isStrikethrough
224
351
 
352
+ // lineSpacing is added on top of the font's natural line height, not on top of fontSize.
353
+ let lineSpacing = max(0, fontScale(lineHeight)
354
+ - momoLineHeight(
355
+ size: fontScale(fontSize),
356
+ family: fontFamily ?? theme.font,
357
+ fallbackFamily: theme.font,
358
+ isWhiteList: designSystemKitsConfigs.isWhiteList
359
+ ))
360
+
225
361
  var text = baseText
226
- .font(momoFont(size: fontScale(typography.fontSize), weight: typography.fontWeight, family: theme.font))
362
+ .font(font)
227
363
  .foregroundColor(textColor)
228
364
  .strikethrough(isStrikethrough, color: textColor)
229
365
  .underline(underline, color: textColor)
@@ -234,7 +370,7 @@ public struct MomoText: View {
234
370
 
235
371
  return text
236
372
  .multilineTextAlignment(textAlign)
237
- .lineSpacing(fontScale(typography.lineHeight) - fontScale(typography.fontSize))
373
+ .lineSpacing(lineSpacing)
238
374
  .modifier(LineLimitModifier(range: lineLimitRange))
239
375
  .truncationMode(truncationMode)
240
376
  .fixedSize(horizontal: !softWrap, vertical: false)