@momo-kits/native-kits 0.163.1-beta.8 → 0.163.1-beta.9-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 (334) hide show
  1. package/CLAUDE.md +1 -1
  2. package/build.gradle.kts +11 -0
  3. package/compose/build.gradle.kts +190 -0
  4. package/compose/build.gradle.kts.backup +190 -0
  5. package/compose/compose.podspec +47 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +150 -0
  9. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  10. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +106 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +294 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +112 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +477 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +214 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +276 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +65 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +48 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +51 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +253 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +100 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +172 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +360 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +581 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +156 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +128 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +69 -0
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  132. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
  133. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  134. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +207 -0
  135. package/gradle/libs.versions.toml +69 -0
  136. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  137. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  138. package/gradle.properties +26 -0
  139. package/gradlew +252 -0
  140. package/gradlew.bat +94 -0
  141. package/ios/Application/ApplicationEnvironment.swift +15 -2
  142. package/ios/Application/Components.swift +50 -44
  143. package/ios/Application/FloatingButton.swift +10 -9
  144. package/ios/Application/HeaderRight.swift +79 -45
  145. package/ios/Application/Localize.swift +277 -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 +200 -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/Screen.swift +2 -1
  164. package/ios/Application/StackScreen.swift +501 -0
  165. package/ios/Avatar/Avatar.swift +193 -0
  166. package/ios/Badge/Badge.swift +11 -14
  167. package/ios/Badge/BadgeRibbon.swift +92 -21
  168. package/ios/BaselineView/BaselineView.swift +163 -0
  169. package/ios/Button/Button.swift +124 -29
  170. package/ios/Carousel/Carousel.swift +196 -0
  171. package/ios/Checkbox/Checkbox.swift +23 -28
  172. package/ios/Chip/Chip.swift +56 -33
  173. package/ios/Collapse/Collapse.swift +192 -0
  174. package/ios/Colors+Radius+Spacing/Colors.swift +20 -17
  175. package/ios/Colors+Radius+Spacing/Spacing.swift +3 -1
  176. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  177. package/ios/DateTimePicker/DateTimePicker.swift +212 -0
  178. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  179. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  180. package/ios/DateTimePicker/WheelPicker.swift +165 -0
  181. package/ios/Divider/Divider.swift +16 -0
  182. package/ios/Extensions/ActiveOpacityButtonStyle.swift +14 -0
  183. package/ios/Extensions/Color++.swift +32 -0
  184. package/ios/Icon/Icon.swift +10 -1
  185. package/ios/IconButton/IconButton.swift +140 -0
  186. package/ios/Image/Image.swift +16 -3
  187. package/ios/Information/Information.swift +3 -1
  188. package/ios/Input/ErrorView.swift +3 -1
  189. package/ios/Input/Input.swift +72 -33
  190. package/ios/Input/InputPhoneNumber.swift +33 -21
  191. package/ios/Input/InputSearch.swift +107 -48
  192. package/ios/Input/InputTextArea.swift +134 -64
  193. package/ios/InputDropDown/InputDropDown.swift +201 -0
  194. package/ios/InputMoney/InputMoney.swift +347 -0
  195. package/ios/InputOTP/InputOTP.swift +210 -0
  196. package/ios/Loader/Loader.swift +113 -0
  197. package/ios/MoMoUIKits.podspec +5 -0
  198. package/ios/Pagination/PaginationDot.swift +61 -0
  199. package/ios/Pagination/PaginationNumber.swift +35 -0
  200. package/ios/Pagination/PaginationScroll.swift +101 -0
  201. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  202. package/ios/Popup/PopupDisplay.swift +11 -20
  203. package/ios/Popup/PopupInput.swift +2 -8
  204. package/ios/Popup/PopupNotify.swift +218 -0
  205. package/ios/Popup/PopupPromotion.swift +16 -8
  206. package/ios/ProgressInfo/ProgressInfo.swift +296 -0
  207. package/ios/Radio/Radio.swift +69 -0
  208. package/ios/Rating/Rating.swift +82 -0
  209. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +11 -0
  210. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +38 -0
  211. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +38 -0
  212. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +38 -0
  213. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +6 -0
  214. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +38 -0
  215. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +38 -0
  216. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +38 -0
  217. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +38 -0
  218. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +38 -0
  219. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +38 -0
  220. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +38 -0
  221. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +38 -0
  222. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
  223. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
  224. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
  225. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
  226. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
  227. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
  228. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
  229. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
  230. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
  231. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
  232. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
  233. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
  234. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
  235. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
  236. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
  237. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
  238. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
  239. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
  240. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
  241. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +38 -0
  242. package/ios/Skeleton/Skeleton.swift +99 -0
  243. package/ios/Slider/Slider.swift +237 -0
  244. package/ios/Stepper/Stepper.swift +220 -0
  245. package/ios/Steps/Steps.swift +467 -0
  246. package/ios/SuggestAction/SuggestAction.swift +99 -0
  247. package/ios/Swipeable/SwipeCell.swift +187 -0
  248. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  249. package/ios/Switch/Switch.swift +53 -28
  250. package/ios/TabView/TabView.swift +534 -0
  251. package/ios/Tag/Tag.swift +98 -0
  252. package/ios/Template/TrustBanner/TrustBanner.swift +6 -7
  253. package/ios/Title/Title.swift +269 -0
  254. package/ios/Tooltip/Tooltip.swift +485 -0
  255. package/ios/Typography/FontScaleStore.swift +19 -0
  256. package/ios/Typography/Text.swift +79 -5
  257. package/ios/Typography/Typography.swift +3 -0
  258. package/ios/Uploader/Uploader.swift +174 -0
  259. package/ios/native-kits.podspec +61 -8
  260. package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
  261. package/ios-demo/README.md +120 -0
  262. package/ios-demo/Resources/dot_loading.json +558 -0
  263. package/ios-demo/Resources/icon.json +4052 -0
  264. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  265. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  266. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  267. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  268. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  269. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  270. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  271. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  272. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  273. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  274. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  275. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
  276. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  277. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  278. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  279. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  280. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  281. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  282. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  283. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +63 -0
  284. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  285. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  286. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  287. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  288. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  289. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  290. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +279 -0
  291. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  292. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  293. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  294. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  295. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  296. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  297. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  298. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  299. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  300. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  301. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  302. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +188 -0
  303. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +229 -0
  304. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  305. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  306. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  307. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  308. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  309. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  310. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  311. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  312. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  313. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  314. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  315. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  316. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  317. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  318. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  319. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  320. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  321. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  322. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  323. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  324. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  325. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  326. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +51 -0
  327. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  328. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  329. package/local.properties +8 -0
  330. package/package.json +1 -1
  331. package/publish.sh +53 -0
  332. package/scripts/gen-ios-color.mjs +136 -0
  333. package/settings.gradle.kts +64 -0
  334. package/.claude/settings.local.json +0 -62
@@ -0,0 +1,485 @@
1
+ import SwiftUI
2
+
3
+ // MARK: - Types
4
+
5
+ /// Tooltip placement relative to the anchor element.
6
+ public enum TooltipPlacement {
7
+ case top
8
+ case bottom
9
+ case left
10
+ case right
11
+ }
12
+
13
+ /// Cross-axis alignment of the tooltip relative to the anchor.
14
+ public enum TooltipAlign {
15
+ case start
16
+ case center
17
+ case end
18
+ }
19
+
20
+ /// Describes a single action button displayed in the tooltip.
21
+ public struct TooltipButton {
22
+ public let title: String?
23
+ public let icon: String?
24
+ public let onPress: (() -> Void)?
25
+
26
+ public init(
27
+ title: String? = nil,
28
+ icon: String? = nil,
29
+ onPress: (() -> Void)? = nil
30
+ ) {
31
+ self.title = title
32
+ self.icon = icon
33
+ self.onPress = onPress
34
+ }
35
+ }
36
+
37
+ /// State holder for controlling tooltip visibility imperatively.
38
+ /// Mirrors the Compose `TooltipState` / `rememberTooltipState()` pattern.
39
+ public final class TooltipState: ObservableObject {
40
+ @Published public private(set) var isVisible: Bool = false
41
+
42
+ public init() {}
43
+
44
+ /// Shows the tooltip.
45
+ public func show() {
46
+ isVisible = true
47
+ }
48
+
49
+ /// Hides the tooltip.
50
+ public func hide() {
51
+ isVisible = false
52
+ }
53
+
54
+ /// Toggles the tooltip visibility.
55
+ public func toggle() {
56
+ isVisible.toggle()
57
+ }
58
+ }
59
+
60
+ // MARK: - Constants
61
+
62
+ private enum TooltipConst {
63
+ static let offset = Spacing.S // TOOLTIP_OFFSET
64
+ static let arrowSize: CGFloat = 6 // ARROW_SIZE
65
+ static let maxWidth: CGFloat = 300
66
+ static let cornerRadius = Radius.S
67
+ static let arrowCornerRadius: CGFloat = 2
68
+ static let arrowEdgeMargin = Spacing.M
69
+ static let arrowOverlap: CGFloat = 1
70
+ }
71
+
72
+ // MARK: - Arrow Shape
73
+
74
+ /// Triangular arrow pointing away from the anchor in the given placement direction.
75
+ struct TooltipArrowShape: Shape {
76
+ let placement: TooltipPlacement
77
+
78
+ func path(in rect: CGRect) -> Path {
79
+ let w = rect.width
80
+ let h = rect.height
81
+ var path = Path()
82
+ switch placement {
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))
87
+ 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))
91
+ 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))
95
+ 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))
99
+ }
100
+ path.closeSubpath()
101
+ return path
102
+ }
103
+ }
104
+
105
+ // MARK: - Anchor bounds preference
106
+
107
+ private struct TooltipAnchorBoundsKey: PreferenceKey {
108
+ static var defaultValue: Anchor<CGRect>? = nil
109
+ static func reduce(value: inout Anchor<CGRect>?, nextValue: () -> Anchor<CGRect>?) {
110
+ value = nextValue() ?? value
111
+ }
112
+ }
113
+
114
+ // MARK: - Tooltip
115
+
116
+ /// A tooltip component that wraps `content` (the anchor) and shows a positioned
117
+ /// tooltip popup with title, description, close button, and action buttons.
118
+ ///
119
+ /// Port of the Compose / React Native `Tooltip` component.
120
+ public struct Tooltip<Content: View>: View {
121
+ @ObservedObject private var state: TooltipState
122
+ private let title: String?
123
+ private let description: String
124
+ private let buttons: [TooltipButton]
125
+ private let placement: TooltipPlacement
126
+ private let align: TooltipAlign
127
+ private let onVisibleChange: ((Bool) -> Void)?
128
+ private let onPressClose: (() -> Void)?
129
+ private let content: Content
130
+
131
+ public init(
132
+ state: TooltipState,
133
+ title: String? = nil,
134
+ description: String,
135
+ buttons: [TooltipButton] = [],
136
+ placement: TooltipPlacement = .top,
137
+ align: TooltipAlign = .center,
138
+ onVisibleChange: ((Bool) -> Void)? = nil,
139
+ onPressClose: (() -> Void)? = nil,
140
+ @ViewBuilder content: () -> Content
141
+ ) {
142
+ self.state = state
143
+ self.title = title
144
+ self.description = description
145
+ self.buttons = buttons
146
+ self.placement = placement
147
+ self.align = align
148
+ self.onVisibleChange = onVisibleChange
149
+ self.onPressClose = onPressClose
150
+ self.content = content()
151
+ }
152
+
153
+ public var body: some View {
154
+ content
155
+ .anchorPreference(key: TooltipAnchorBoundsKey.self, value: .bounds) { $0 }
156
+ .overlayPreferenceValue(TooltipAnchorBoundsKey.self) { anchor in
157
+ GeometryReader { proxy in
158
+ if state.isVisible, let anchor = anchor {
159
+ let anchorRect = proxy[anchor]
160
+ TooltipPopupContent(
161
+ title: title,
162
+ description: description,
163
+ buttons: buttons,
164
+ placement: placement,
165
+ align: align,
166
+ onPressClose: onPressClose
167
+ )
168
+ .fixedSize()
169
+ .modifier(
170
+ TooltipPositionModifier(
171
+ anchorRect: anchorRect,
172
+ containerSize: proxy.size,
173
+ placement: placement,
174
+ align: align
175
+ )
176
+ )
177
+ }
178
+ }
179
+ }
180
+ .onChange(of: state.isVisible) { newValue in
181
+ onVisibleChange?(newValue)
182
+ }
183
+ }
184
+ }
185
+
186
+ // MARK: - Positioning
187
+
188
+ /// Positions the tooltip (already sized via `.fixedSize()`) relative to the anchor,
189
+ /// mirroring `TooltipPositionProvider.calculatePosition`.
190
+ private struct TooltipPositionModifier: ViewModifier {
191
+ let anchorRect: CGRect
192
+ let containerSize: CGSize
193
+ let placement: TooltipPlacement
194
+ let align: TooltipAlign
195
+
196
+ @State private var popupSize: CGSize = .zero
197
+
198
+ private var offsetPx: CGFloat {
199
+ TooltipConst.offset + TooltipConst.arrowSize
200
+ }
201
+
202
+ func body(content: Content) -> some View {
203
+ content
204
+ .background(
205
+ GeometryReader { p in
206
+ Color.clear
207
+ .onAppear { popupSize = p.size }
208
+ .onChange(of: p.size) { popupSize = $0 }
209
+ }
210
+ )
211
+ .offset(x: originX, y: originY)
212
+ }
213
+
214
+ private var originX: CGFloat {
215
+ let popW = popupSize.width
216
+ switch placement {
217
+ case .top, .bottom:
218
+ return alignHorizontal(anchorRect.minX, anchorRect.width, popW)
219
+ case .left:
220
+ return anchorRect.minX - popW - offsetPx
221
+ case .right:
222
+ return anchorRect.maxX + offsetPx
223
+ }
224
+ }
225
+
226
+ private var originY: CGFloat {
227
+ let popH = popupSize.height
228
+ switch placement {
229
+ case .top:
230
+ return anchorRect.minY - popH - offsetPx
231
+ case .bottom:
232
+ return anchorRect.maxY + offsetPx
233
+ case .left, .right:
234
+ return alignVertical(anchorRect.minY, anchorRect.height, popH)
235
+ }
236
+ }
237
+
238
+ private func alignHorizontal(_ anchorX: CGFloat, _ anchorW: CGFloat, _ popW: CGFloat) -> CGFloat {
239
+ switch align {
240
+ case .start: return anchorX
241
+ case .end: return anchorX + anchorW - popW
242
+ case .center: return anchorX + (anchorW - popW) / 2
243
+ }
244
+ }
245
+
246
+ private func alignVertical(_ anchorY: CGFloat, _ anchorH: CGFloat, _ popH: CGFloat) -> CGFloat {
247
+ switch align {
248
+ case .start: return anchorY
249
+ case .end: return anchorY + anchorH - popH
250
+ case .center: return anchorY + (anchorH - popH) / 2
251
+ }
252
+ }
253
+ }
254
+
255
+ // MARK: - Popup content
256
+
257
+ private struct TooltipPopupContent: View {
258
+ let title: String?
259
+ let description: String?
260
+ let buttons: [TooltipButton]
261
+ let placement: TooltipPlacement
262
+ let align: TooltipAlign
263
+ let onPressClose: (() -> Void)?
264
+
265
+ var body: some View {
266
+ ZStack {
267
+ VStack(alignment: .leading, spacing: 0) {
268
+ HStack(alignment: .top, spacing: 0) {
269
+ VStack(alignment: .leading, spacing: 0) {
270
+ if let title = title, !title.isEmpty {
271
+ MomoText(title, typography: .headerSSemibold, color: Colors.black01)
272
+ .lineLimit(1)
273
+ .truncationMode(.tail)
274
+ .padding(.bottom, Spacing.XS)
275
+ }
276
+ if let description = description, !description.isEmpty {
277
+ MomoText(description, typography: .descriptionDefaultRegular, color: Colors.black01)
278
+ .lineLimit(2)
279
+ .truncationMode(.tail)
280
+ }
281
+ }
282
+ .frame(maxWidth: .infinity, alignment: .leading)
283
+
284
+ if let onPressClose = onPressClose {
285
+ Spacer().frame(width: Spacing.M)
286
+ SwiftUI.Button(action: onPressClose) {
287
+ Icon(source: "navigation_close", size: 20, color: Colors.black01)
288
+ .frame(width: 20, height: 20)
289
+ }
290
+ .buttonStyle(.plain)
291
+ }
292
+ }
293
+
294
+ if !buttons.isEmpty {
295
+ Spacer().frame(height: Spacing.M)
296
+ TooltipButtons(buttons: buttons)
297
+ .frame(maxWidth: .infinity, alignment: .trailing)
298
+ }
299
+ }
300
+ .padding(Spacing.M)
301
+ .frame(maxWidth: TooltipConst.maxWidth, alignment: .leading)
302
+ .background(Colors.black17)
303
+ .clipShape(RoundedRectangle(cornerRadius: TooltipConst.cornerRadius))
304
+ }
305
+ .overlay(arrow, alignment: arrowAlignment)
306
+ }
307
+
308
+ // MARK: Arrow
309
+
310
+ @ViewBuilder
311
+ private var arrow: some View {
312
+ let arrowWidth = TooltipConst.arrowSize * 2
313
+ let arrowHeight = TooltipConst.arrowSize
314
+ let isVertical = placement == .top || placement == .bottom
315
+ TooltipArrowShape(placement: placement)
316
+ .fill(Colors.black17)
317
+ .frame(
318
+ width: isVertical ? arrowWidth : arrowHeight,
319
+ height: isVertical ? arrowHeight : arrowWidth
320
+ )
321
+ .offset(x: arrowOffsetX, y: arrowOffsetY)
322
+ }
323
+
324
+ private var arrowAlignment: Alignment {
325
+ switch placement {
326
+ case .top:
327
+ switch align {
328
+ case .start: return .bottomLeading
329
+ case .center: return .bottom
330
+ case .end: return .bottomTrailing
331
+ }
332
+ case .bottom:
333
+ switch align {
334
+ case .start: return .topLeading
335
+ case .center: return .top
336
+ case .end: return .topTrailing
337
+ }
338
+ case .left:
339
+ switch align {
340
+ case .start: return .topTrailing
341
+ case .center: return .trailing
342
+ case .end: return .bottomTrailing
343
+ }
344
+ case .right:
345
+ switch align {
346
+ case .start: return .topLeading
347
+ case .center: return .leading
348
+ case .end: return .bottomLeading
349
+ }
350
+ }
351
+ }
352
+
353
+ private var arrowOffsetX: CGFloat {
354
+ let arrowHeight = TooltipConst.arrowSize
355
+ switch placement {
356
+ case .left: return arrowHeight - TooltipConst.arrowOverlap
357
+ case .right: return -arrowHeight + TooltipConst.arrowOverlap
358
+ default:
359
+ switch align {
360
+ case .start: return TooltipConst.arrowEdgeMargin
361
+ case .end: return -TooltipConst.arrowEdgeMargin
362
+ case .center: return 0
363
+ }
364
+ }
365
+ }
366
+
367
+ private var arrowOffsetY: CGFloat {
368
+ let arrowHeight = TooltipConst.arrowSize
369
+ switch placement {
370
+ case .top: return arrowHeight - TooltipConst.arrowOverlap
371
+ case .bottom: return -arrowHeight + TooltipConst.arrowOverlap
372
+ default:
373
+ switch align {
374
+ case .start: return TooltipConst.arrowEdgeMargin
375
+ case .end: return -TooltipConst.arrowEdgeMargin
376
+ case .center: return 0
377
+ }
378
+ }
379
+ }
380
+ }
381
+
382
+ // MARK: - Buttons row
383
+
384
+ private struct TooltipButtons: View {
385
+ let buttons: [TooltipButton]
386
+
387
+ var body: some View {
388
+ HStack(spacing: 0) {
389
+ content
390
+ }
391
+ }
392
+
393
+ @ViewBuilder
394
+ private var content: some View {
395
+ if buttons.count == 1 {
396
+ TooltipSingleButton(btn: buttons[0])
397
+ } else if buttons.count == 2 {
398
+ let primary = buttons[0]
399
+ let secondary = buttons[1]
400
+ let bothIcons = primary.icon != nil && secondary.icon != nil
401
+ if bothIcons {
402
+ TooltipIconButton(icon: secondary.icon ?? "", onPress: secondary.onPress ?? {})
403
+ Spacer().frame(width: Spacing.S)
404
+ TooltipIconButton(icon: primary.icon ?? "", onPress: primary.onPress ?? {})
405
+ } else {
406
+ TooltipSecondaryButton(title: secondary.title ?? "", onPress: secondary.onPress ?? {})
407
+ Spacer().frame(width: Spacing.S)
408
+ TooltipPrimaryButton(title: primary.title ?? "", onPress: primary.onPress ?? {})
409
+ }
410
+ } else {
411
+ ForEach(Array(buttons.enumerated()), id: \.offset) { index, btn in
412
+ if index > 0 {
413
+ Spacer().frame(width: Spacing.XXS)
414
+ }
415
+ TooltipSingleButton(btn: btn)
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ private func tooltipLimitWithEllipsis(_ value: String, max: Int = 16) -> String {
422
+ value.count > max ? String(value.prefix(max)) + "…" : value
423
+ }
424
+
425
+ private struct TooltipSingleButton: View {
426
+ let btn: TooltipButton
427
+
428
+ var body: some View {
429
+ if let icon = btn.icon {
430
+ TooltipIconButton(icon: icon, onPress: btn.onPress ?? {})
431
+ } else {
432
+ TooltipPrimaryButton(title: btn.title ?? "", onPress: btn.onPress ?? {})
433
+ }
434
+ }
435
+ }
436
+
437
+ private struct TooltipPrimaryButton: View {
438
+ let title: String
439
+ let onPress: () -> Void
440
+
441
+ var body: some View {
442
+ Button(
443
+ title: tooltipLimitWithEllipsis(title),
444
+ action: onPress,
445
+ type: .secondary,
446
+ size: .medium,
447
+ isFull: false
448
+ )
449
+ }
450
+ }
451
+
452
+ private struct TooltipSecondaryButton: View {
453
+ let title: String
454
+ let onPress: () -> Void
455
+
456
+ var body: some View {
457
+ SwiftUI.Button(action: onPress) {
458
+ MomoText(tooltipLimitWithEllipsis(title), typography: .actionSBold, color: Colors.black01)
459
+ .lineLimit(1)
460
+ .truncationMode(.tail)
461
+ .padding(.horizontal, Spacing.M)
462
+ .padding(.vertical, Spacing.S)
463
+ }
464
+ .buttonStyle(.plain)
465
+ // Mirrors Compose's `Modifier.weight(1f, fill = false)` on the secondary button:
466
+ // it yields space to the primary button first under width pressure.
467
+ .layoutPriority(-1)
468
+ }
469
+ }
470
+
471
+ private struct TooltipIconButton: View {
472
+ let icon: String
473
+ let onPress: () -> Void
474
+
475
+ var body: some View {
476
+ let iconButtonSize = scaleSize(36)
477
+ SwiftUI.Button(action: onPress) {
478
+ Icon(source: icon, size: 24)
479
+ .frame(width: iconButtonSize, height: iconButtonSize)
480
+ .background(Colors.black01)
481
+ .clipShape(RoundedRectangle(cornerRadius: Radius.XL))
482
+ }
483
+ .buttonStyle(.plain)
484
+ }
485
+ }
@@ -0,0 +1,19 @@
1
+ import CoreGraphics
2
+
3
+ /// App-wide font-scale preference for the SwiftUI kit. An exported holder the host writes once at
4
+ /// app startup (from the cache-backed `FontScaleProvider`); `scaleSize`/`appFont` read the snapshot.
5
+ /// No realtime: the SwiftUI kit has no navigation container to scope reactive updates, so a startup
6
+ /// snapshot is enough. When `useOSScaleRate` is false the kit applies `userScaleRate` instead of the
7
+ /// OS Dynamic Type factor.
8
+ public final class FontScaleStore {
9
+ public static let shared = FontScaleStore()
10
+ private init() {}
11
+
12
+ public private(set) var userScaleRate: CGFloat = 1
13
+ public private(set) var useOSScaleRate: Bool = true
14
+
15
+ public func update(userScaleRate: CGFloat, useOSScaleRate: Bool) {
16
+ self.userScaleRate = userScaleRate
17
+ self.useOSScaleRate = useOSScaleRate
18
+ }
19
+ }
@@ -1,6 +1,9 @@
1
1
  import SwiftUI
2
2
 
3
3
  public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat? = nil) -> CGFloat {
4
+ if !FontScaleStore.shared.useOSScaleRate {
5
+ return size * FontScaleStore.shared.userScaleRate
6
+ }
4
7
  let defaultScreenSize: CGFloat = 375
5
8
  let maxFontScale: CGFloat = scaleRate ?? 1.5
6
9
  let maxDeviceScale: CGFloat = 5
@@ -115,27 +118,98 @@ public extension TypographyStyle {
115
118
  }
116
119
 
117
120
  public struct MomoText: View {
121
+ @Environment(\.appTheme) private var theme
118
122
  private let content: String
119
123
  private let typography: TypographyStyle
120
- private let color: Color
124
+ private let color: Color?
125
+ private let textAlign: TextAlignment
126
+ private let maxLines: Int
127
+ private let minLines: Int
128
+ private let lineLimitRange: ClosedRange<Int>?
129
+ private let truncationMode: Text.TruncationMode
130
+ private let strikethrough: Bool
131
+ private let underline: Bool
132
+ private let letterSpacing: CGFloat?
133
+ private let softWrap: Bool
134
+ private let accessibilityId: String?
121
135
 
122
136
  public init(
123
137
  _ content: String,
124
138
  typography: TypographyStyle = .bodyDefaultRegular,
125
- color: Color = Colors.text
139
+ color: Color? = nil,
140
+ textAlign: TextAlignment = .leading,
141
+ maxLines: Int = Int.max,
142
+ minLines: Int = 1,
143
+ truncationMode: Text.TruncationMode = .tail,
144
+ strikethrough: Bool = false,
145
+ underline: Bool = false,
146
+ letterSpacing: CGFloat? = nil,
147
+ softWrap: Bool = true,
148
+ accessibilityId: String? = nil
126
149
  ) {
127
150
  self.content = content
128
151
  self.typography = typography
129
152
  self.color = color
153
+ self.textAlign = textAlign
154
+ self.maxLines = maxLines
155
+ self.minLines = minLines
156
+ // SwiftUI's range-based `lineLimit` is iOS 16+ only; keep the range for
157
+ // newer OS versions and fall back to the legacy single-value overload.
158
+ if maxLines == Int.max && minLines <= 1 {
159
+ self.lineLimitRange = nil
160
+ } else {
161
+ let upper = maxLines == Int.max ? Int.max : Swift.max(maxLines, minLines)
162
+ self.lineLimitRange = Swift.max(1, minLines)...upper
163
+ }
164
+ self.truncationMode = truncationMode
165
+ self.strikethrough = strikethrough
166
+ self.underline = underline
167
+ self.letterSpacing = letterSpacing
168
+ self.softWrap = softWrap
169
+ self.accessibilityId = accessibilityId
130
170
  }
131
171
 
132
172
  public var body: some View {
133
- let text = SwiftUI.Text(content)
173
+ let textColor = color ?? theme.colors.text.default
174
+ // Strikethrough is driven either by the typography variant (mirrors Compose
175
+ // *Strikethrough styles) or the explicit `strikethrough` flag.
176
+ let isStrikethrough = strikethrough || typography.isStrikethrough
177
+
178
+ var text = SwiftUI.Text(content)
134
179
  .font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
135
- .foregroundColor(color)
136
- .lineSpacing(scaleSize(typography.lineHeight) - scaleSize(typography.fontSize))
180
+ .foregroundColor(textColor)
181
+ .strikethrough(isStrikethrough, color: textColor)
182
+ .underline(underline, color: textColor)
183
+
184
+ if let letterSpacing = letterSpacing {
185
+ text = text.tracking(letterSpacing)
186
+ }
137
187
 
138
188
  return text
189
+ .multilineTextAlignment(textAlign)
190
+ .lineSpacing(scaleSize(typography.lineHeight) - scaleSize(typography.fontSize))
191
+ .modifier(LineLimitModifier(range: lineLimitRange))
192
+ .truncationMode(truncationMode)
193
+ .fixedSize(horizontal: !softWrap, vertical: false)
194
+ .accessibilityIdentifier(accessibilityId ?? content)
139
195
  .ignoreBoldTextSetting()
140
196
  }
141
197
  }
198
+
199
+ // Applies a line-limit range only on iOS 16+. Older targets use the legacy
200
+ // single-value overload so the file still compiles for iOS 15.
201
+ private struct LineLimitModifier: ViewModifier {
202
+ let range: ClosedRange<Int>?
203
+
204
+ func body(content: Content) -> some View {
205
+ if let range = range {
206
+ if #available(iOS 16.0, *) {
207
+ content.lineLimit(range)
208
+ } else {
209
+ content.lineLimit(range.upperBound)
210
+ }
211
+ } else {
212
+ content
213
+ }
214
+ }
215
+ }
@@ -11,6 +11,9 @@ public extension View {
11
11
 
12
12
  public extension Font {
13
13
  static func appFont(size: CGFloat) -> Font {
14
+ if !FontScaleStore.shared.useOSScaleRate {
15
+ return Font.system(size: size * FontScaleStore.shared.userScaleRate)
16
+ }
14
17
  let defaultScreenSize: CGFloat = 375
15
18
  let maxFontScale: CGFloat = 1.5
16
19
  let maxDeviceScale: CGFloat = 5