@momo-kits/native-kits 0.0.1-beta.4 → 0.0.2-alpha.1

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 (296) hide show
  1. package/CLAUDE.md +99 -0
  2. package/PUBLISH_GUIDE.md +675 -0
  3. package/README.md +184 -5
  4. package/ios/Application/ApplicationEnvironment.swift +71 -0
  5. package/ios/Application/Components.swift +270 -0
  6. package/ios/Application/ComposeApi.swift +22 -0
  7. package/ios/Application/FloatingButton.swift +174 -0
  8. package/ios/Application/HeaderRight.swift +306 -0
  9. package/ios/Application/LanguageSource.swift +93 -0
  10. package/ios/Application/Localize.swift +378 -0
  11. package/ios/Application/LocalizeModifier.swift +30 -0
  12. package/ios/Application/MaxApi.swift +18 -0
  13. package/ios/Application/Navigation/BottomTab/BottomTab.swift +104 -0
  14. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  15. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  16. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  17. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  18. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  19. package/ios/Application/Navigation/NavigationContainer.swift +213 -0
  20. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  21. package/ios/Application/Navigation/Navigator.swift +453 -0
  22. package/ios/Application/Navigation/NavigatorManager.swift +22 -0
  23. package/ios/Application/Navigation/Overplay/BottomSheet.swift +256 -0
  24. package/ios/Application/Navigation/Overplay/ModalScreen.swift +74 -0
  25. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  26. package/ios/Application/Navigation/Tracking/ScreenTracker.swift +117 -0
  27. package/ios/Application/Navigation/component/Header.swift +178 -0
  28. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  29. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  30. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +11 -0
  31. package/ios/Application/Navigation/component/NavigationFooter.swift +90 -0
  32. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +196 -0
  33. package/ios/Application/Screen.swift +250 -0
  34. package/ios/Application/StackScreen.swift +492 -0
  35. package/ios/Avatar/Avatar.swift +193 -0
  36. package/ios/Badge/Badge.swift +84 -0
  37. package/ios/Badge/BadgeDot.swift +31 -0
  38. package/ios/Badge/BadgeRibbon.swift +319 -0
  39. package/ios/BaselineView/BaselineView.swift +163 -0
  40. package/ios/Button/Button.swift +232 -84
  41. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
  42. package/ios/Carousel/Carousel.swift +196 -0
  43. package/ios/Checkbox/Checkbox.swift +77 -0
  44. package/ios/Chip/Chip.swift +120 -0
  45. package/ios/Collapse/Collapse.swift +192 -0
  46. package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Colors.swift +42 -37
  47. package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
  48. package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Spacing.swift +3 -1
  49. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  50. package/ios/DateTimePicker/DateTimePicker.swift +212 -0
  51. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  52. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  53. package/ios/DateTimePicker/WheelPicker.swift +165 -0
  54. package/ios/Divider/Divider.swift +16 -0
  55. package/ios/Extensions/ActiveOpacityButtonStyle.swift +14 -0
  56. package/ios/Extensions/Color++.swift +32 -0
  57. package/ios/Icon/Icon.swift +69 -0
  58. package/ios/IconButton/IconButton.swift +140 -0
  59. package/ios/Image/Image.swift +87 -0
  60. package/ios/Information/Information.swift +156 -0
  61. package/ios/Input/ErrorView.swift +58 -0
  62. package/ios/Input/Input.swift +461 -77
  63. package/ios/Input/InputPhoneNumber.swift +192 -0
  64. package/ios/Input/InputSearch.swift +301 -0
  65. package/ios/Input/InputTextArea.swift +318 -0
  66. package/ios/InputDropDown/InputDropDown.swift +201 -0
  67. package/ios/InputMoney/InputMoney.swift +405 -0
  68. package/ios/InputOTP/InputOTP.swift +211 -0
  69. package/ios/Layout/GridContext.swift +30 -0
  70. package/ios/Layout/Item.swift +42 -0
  71. package/ios/Layout/Section.swift +134 -0
  72. package/ios/Loader/Loader.swift +113 -0
  73. package/ios/Lottie/LottieView.swift +86 -0
  74. package/ios/MoMoUIKits.podspec +5 -0
  75. package/ios/OTPKeyboard/KeyboardButton.swift +13 -5
  76. package/ios/OTPKeyboard/OTPKeyboard.swift +1 -1
  77. package/ios/Pagination/PaginationDot.swift +61 -0
  78. package/ios/Pagination/PaginationNumber.swift +35 -0
  79. package/ios/Pagination/PaginationScroll.swift +101 -0
  80. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  81. package/ios/Popup/PopupDisplay.swift +279 -0
  82. package/ios/Popup/PopupInput.swift +92 -0
  83. package/ios/Popup/PopupNotify.swift +219 -0
  84. package/ios/Popup/PopupPromotion.swift +74 -0
  85. package/ios/PopupView/FullscreenPopup.swift +251 -0
  86. package/ios/{Popup → PopupView}/Modifiers.swift +6 -20
  87. package/ios/{Popup → PopupView}/PopupView.swift +38 -146
  88. package/ios/PopupView/Utils++.swift +281 -0
  89. package/ios/ProgressInfo/ProgressInfo.swift +297 -0
  90. package/ios/Radio/Radio.swift +69 -0
  91. package/ios/Rating/Rating.swift +83 -0
  92. package/{example/ios/Example/Preview Content/Preview Assets.xcassets/Contents.json → ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json} +5 -0
  93. package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Text.colorset/Contents.json +3 -3
  94. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
  95. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
  96. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
  97. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
  98. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
  99. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
  100. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
  101. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
  102. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
  103. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
  104. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
  105. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
  106. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
  107. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
  108. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
  109. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
  110. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
  111. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
  112. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
  113. package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
  114. package/ios/Skeleton/Skeleton.swift +99 -0
  115. package/ios/Slider/Slider.swift +237 -0
  116. package/ios/StatusBarTap/StatusBarTap.h +13 -0
  117. package/ios/StatusBarTap/StatusBarTap.m +75 -0
  118. package/ios/Stepper/Stepper.swift +221 -0
  119. package/ios/Steps/Steps.swift +467 -0
  120. package/ios/SuggestAction/SuggestAction.swift +100 -0
  121. package/ios/Swipeable/SwipeCell.swift +226 -31
  122. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  123. package/ios/Switch/Switch.swift +69 -0
  124. package/ios/TabView/TabView.swift +534 -0
  125. package/ios/Tag/Tag.swift +99 -0
  126. package/ios/Template/Logo/Logo.swift +75 -0
  127. package/ios/Template/TrustBanner/TrustBanner.swift +123 -0
  128. package/ios/Title/Title.swift +270 -0
  129. package/ios/Tooltip/Tooltip.swift +486 -0
  130. package/ios/Typography/FontScale.swift +103 -0
  131. package/ios/Typography/FontScaleStore.swift +58 -0
  132. package/ios/Typography/KitDesignSystem.swift +17 -0
  133. package/ios/Typography/Text.swift +262 -0
  134. package/ios/Typography/Typography.swift +80 -23
  135. package/ios/Uploader/Uploader.swift +174 -0
  136. package/ios/native-kits.podspec +73 -13
  137. package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
  138. package/ios-demo/Resources/dot_loading.json +558 -0
  139. package/ios-demo/Resources/icon.json +4052 -0
  140. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  141. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  142. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  143. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  144. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  145. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  146. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  147. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  148. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  149. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  150. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  151. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
  152. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  153. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +107 -0
  154. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +75 -0
  155. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  156. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  157. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +108 -0
  158. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +96 -0
  159. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +60 -0
  160. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +76 -0
  161. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +102 -0
  162. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  163. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +100 -0
  164. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +420 -0
  165. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +56 -0
  166. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  167. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +276 -0
  168. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +93 -0
  169. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +81 -0
  170. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +65 -0
  171. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +96 -0
  172. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +117 -0
  173. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +94 -0
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +94 -0
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +88 -0
  176. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +120 -0
  177. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +85 -0
  178. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +87 -0
  179. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +73 -0
  180. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +235 -0
  181. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +241 -0
  182. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +68 -0
  183. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
  184. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  185. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +95 -0
  186. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +67 -0
  187. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +101 -0
  188. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +64 -0
  189. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +68 -0
  190. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +83 -0
  191. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +61 -0
  192. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +90 -0
  193. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +82 -0
  194. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +93 -0
  195. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +114 -0
  196. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +90 -0
  197. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +107 -0
  198. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +58 -0
  199. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +114 -0
  200. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +86 -0
  201. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +181 -0
  202. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +104 -0
  203. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +118 -0
  204. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  205. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +85 -0
  206. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +53 -0
  207. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  208. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  209. package/package.json +6 -6
  210. package/publish.sh +554 -20
  211. package/scripts/gen-ios-color.mjs +136 -0
  212. package/CODE_OF_CONDUCT.md +0 -133
  213. package/CONTRIBUTING.md +0 -114
  214. package/LICENSE +0 -20
  215. package/android/build.gradle +0 -82
  216. package/android/gradle.properties +0 -5
  217. package/android/src/main/AndroidManifest.xml +0 -4
  218. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsPackage.kt +0 -17
  219. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsViewManager.kt +0 -20
  220. package/example/ios/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -63
  221. package/example/ios/Example/ComponentDetail.swift +0 -35
  222. package/example/ios/Example/ComponentItem.swift +0 -24
  223. package/example/ios/Example/ComponentModel.swift +0 -33
  224. package/example/ios/Example/ContentView.swift +0 -53
  225. package/example/ios/Example/Example.entitlements +0 -10
  226. package/example/ios/Example/Foundation/ButtonDemo.swift +0 -80
  227. package/example/ios/Example/Foundation/Popup/ActionSheets.swift +0 -136
  228. package/example/ios/Example/Foundation/Popup/Floats.swift +0 -127
  229. package/example/ios/Example/Foundation/Popup/Popups.swift +0 -101
  230. package/example/ios/Example/Foundation/PopupDemo.swift +0 -366
  231. package/example/ios/Example/Foundation/TextInputDemo.swift +0 -31
  232. package/example/ios/Example/Foundation/TypographyDemo.swift +0 -53
  233. package/example/ios/Example/Foundation.swift +0 -26
  234. package/example/ios/Example/Info.plist +0 -18
  235. package/example/ios/Example/Library/SwipeableDemo.swift +0 -105
  236. package/example/ios/Example/Library.swift +0 -26
  237. package/example/ios/Example/Main.swift +0 -11
  238. package/example/ios/Example/Overview.swift +0 -20
  239. package/example/ios/Example/Resource/SFProText-Black.ttf +0 -0
  240. package/example/ios/Example/Resource/SFProText-Bold.ttf +0 -0
  241. package/example/ios/Example/Resource/SFProText-Heavy.ttf +0 -0
  242. package/example/ios/Example/Resource/SFProText-Light.ttf +0 -0
  243. package/example/ios/Example/Resource/SFProText-Medium.ttf +0 -0
  244. package/example/ios/Example/Resource/SFProText-Regular.ttf +0 -0
  245. package/example/ios/Example/Resource/SFProText-Semibold.ttf +0 -0
  246. package/example/ios/Example/Resource/SFProText-Thin.ttf +0 -0
  247. package/example/ios/Example/Resource/SFProText-Ultralight.ttf +0 -0
  248. package/example/ios/Example/Resource/foundation.json +0 -33
  249. package/example/ios/Example/Resource/library.json +0 -8
  250. package/example/ios/Example/Utils.swift +0 -30
  251. package/example/ios/Example/ViewModifier/DarkViewModifier.swift +0 -22
  252. package/example/ios/Example.xcodeproj/project.pbxproj +0 -570
  253. package/example/ios/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  254. package/example/ios/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  255. package/example/ios/Example.xcodeproj/project.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  256. package/example/ios/Example.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  257. package/example/ios/Example.xcworkspace/contents.xcworkspacedata +0 -10
  258. package/example/ios/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  259. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  260. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  261. package/example/ios/Podfile +0 -6
  262. package/example/ios/Podfile.lock +0 -16
  263. package/example/ios/Pods/Local Podspecs/MoMoUIKits.podspec.json +0 -29
  264. package/example/ios/Pods/Manifest.lock +0 -16
  265. package/example/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -661
  266. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  267. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  268. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -25
  269. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-dummy.m +0 -5
  270. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-prefix.pch +0 -12
  271. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-umbrella.h +0 -16
  272. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.debug.xcconfig +0 -12
  273. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.modulemap +0 -6
  274. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.release.xcconfig +0 -12
  275. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown +0 -3
  276. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist +0 -29
  277. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m +0 -5
  278. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h +0 -16
  279. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig +0 -14
  280. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap +0 -6
  281. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig +0 -14
  282. package/ios/Colors+Spacing/Radius.swift +0 -11
  283. package/ios/Popup/Utils++.swift +0 -163
  284. package/ios/Theme.md +0 -18
  285. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Background.colorset/Contents.json +0 -0
  286. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Border.colorset/Contents.json +0 -0
  287. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Card.colorset/Contents.json +0 -0
  288. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Contents.json +0 -0
  289. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Error.colorset/Contents.json +0 -0
  290. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Primary.colorset/Contents.json +0 -0
  291. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/PrimaryDark.colorset/Contents.json +0 -0
  292. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/PrimaryLight.colorset/Contents.json +0 -0
  293. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Secondary.colorset/Contents.json +0 -0
  294. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Success.colorset/Contents.json +0 -0
  295. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/TextSecondary.colorset/Contents.json +0 -0
  296. /package/{example/ios/Example/Assets.xcassets → ios/Resources/MoMoUIKitsColors.xcassets}/Warning.colorset/Contents.json +0 -0
@@ -0,0 +1,134 @@
1
+ import SwiftUI
2
+
3
+ /// 12-column section container: horizontal margin, optional background image, and a
4
+ /// wrapping row of children that read their span size from `\.gridContext`.
5
+ /// Mirrors `vn.momo.kits.layout.Section`.
6
+ public struct SectionView<Content: View>: View {
7
+ // MARK: Lifecycle
8
+
9
+ public init(
10
+ useMargin: Bool = true,
11
+ backgroundImage: String? = nil,
12
+ @ViewBuilder content: @escaping () -> Content
13
+ ) {
14
+ self.useMargin = useMargin
15
+ self.backgroundImage = backgroundImage
16
+ self.content = content
17
+ }
18
+
19
+ // MARK: Public
20
+
21
+ public var body: some View {
22
+ flow
23
+ .environment(\.gridContext, grid)
24
+ .frame(maxWidth: .infinity, alignment: .topLeading)
25
+ .background(widthReader)
26
+ .background(background)
27
+ .padding(.horizontal, useMargin ? Spacing.M : 0)
28
+ }
29
+
30
+ // MARK: Private
31
+
32
+ private static var columns: Int { 12 }
33
+ private static var gutter: CGFloat { Spacing.M }
34
+
35
+ private let useMargin: Bool
36
+ private let backgroundImage: String?
37
+ private let content: () -> Content
38
+
39
+ /// Width available *inside* the horizontal margin — the Compose `BoxWithConstraints` equivalent.
40
+ @State private var availableWidth: CGFloat = 0
41
+
42
+ private var grid: GridContext {
43
+ let columns = CGFloat(Self.columns)
44
+ // The trailing `- 1 / columns` mirrors Compose: it absorbs rounding so 12 spans
45
+ // plus their gutters never overflow the row by a sub-point.
46
+ let sizePerSpan = (availableWidth - Self.gutter * (columns - 1)) / columns - 1 / columns
47
+ return GridContext(numberOfColumns: Self.columns, gutter: Self.gutter, sizePerSpan: max(0, sizePerSpan))
48
+ }
49
+
50
+ @ViewBuilder private var flow: some View {
51
+ if #available(iOS 16.0, *) {
52
+ FlowRowLayout(gutter: Self.gutter) { content() }
53
+ } else {
54
+ // No `Layout` protocol before iOS 16: children stack instead of wrapping.
55
+ // Full-width (span 12) items — the common case — look identical.
56
+ VStack(alignment: .leading, spacing: Self.gutter) { content() }
57
+ }
58
+ }
59
+
60
+ @ViewBuilder private var background: some View {
61
+ if let backgroundImage {
62
+ ImageView(backgroundImage, contentMode: .fill).clipped()
63
+ }
64
+ }
65
+
66
+ private var widthReader: some View {
67
+ GeometryReader { proxy in
68
+ Color.clear.preference(key: SectionWidthKey.self, value: proxy.size.width)
69
+ }
70
+ .onPreferenceChange(SectionWidthKey.self) { availableWidth = $0 }
71
+ }
72
+ }
73
+
74
+ private struct SectionWidthKey: PreferenceKey {
75
+ static var defaultValue: CGFloat = 0
76
+
77
+ static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
78
+ value = max(value, nextValue())
79
+ }
80
+ }
81
+
82
+ /// Wrapping row with equal horizontal/vertical spacing — the Compose `FlowRow` equivalent.
83
+ @available(iOS 16.0, *)
84
+ private struct FlowRowLayout: Layout {
85
+ struct Row {
86
+ var items: [(index: Int, size: CGSize)] = []
87
+ var width: CGFloat = 0
88
+ var height: CGFloat = 0
89
+ }
90
+
91
+ let gutter: CGFloat
92
+
93
+ func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) -> CGSize {
94
+ let maxWidth = proposal.width ?? .infinity
95
+ let rows = rows(for: subviews, maxWidth: maxWidth)
96
+ let height = rows.reduce(0) { $0 + $1.height } + gutter * CGFloat(max(0, rows.count - 1))
97
+ return CGSize(width: proposal.width ?? rows.map(\.width).max() ?? 0, height: height)
98
+ }
99
+
100
+ func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) {
101
+ var y = bounds.minY
102
+ for row in rows(for: subviews, maxWidth: bounds.width) {
103
+ var x = bounds.minX
104
+ for item in row.items {
105
+ subviews[item.index].place(
106
+ at: CGPoint(x: x, y: y),
107
+ anchor: .topLeading,
108
+ proposal: ProposedViewSize(item.size)
109
+ )
110
+ x += item.size.width + gutter
111
+ }
112
+ y += row.height + gutter
113
+ }
114
+ }
115
+
116
+ private func rows(for subviews: Subviews, maxWidth: CGFloat) -> [Row] {
117
+ var rows: [Row] = []
118
+ var current = Row()
119
+ for index in subviews.indices {
120
+ let size = subviews[index].sizeThatFits(ProposedViewSize(width: maxWidth, height: nil))
121
+ let widthIfAppended = current.items.isEmpty ? size.width : current.width + gutter + size.width
122
+ // Sub-point tolerance: spans are computed to land exactly on `maxWidth`.
123
+ if !current.items.isEmpty, widthIfAppended > maxWidth + 0.5 {
124
+ rows.append(current)
125
+ current = Row()
126
+ }
127
+ current.width = current.items.isEmpty ? size.width : current.width + gutter + size.width
128
+ current.height = max(current.height, size.height)
129
+ current.items.append((index, size))
130
+ }
131
+ if !current.items.isEmpty { rows.append(current) }
132
+ return rows
133
+ }
134
+ }
@@ -0,0 +1,113 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ /// Loader variants, mirroring Compose `LoaderType`.
5
+ public enum LoaderType {
6
+ case dot
7
+ case spinner
8
+ }
9
+
10
+ /// Native SwiftUI port of the Compose `Loader` component.
11
+ ///
12
+ /// Mirrors Compose `Loader(type:, tintColor:, modifier:)` which delegates to
13
+ /// `DotLoader` / `SpinnerLoader`. Both variants render Lottie animations
14
+ /// (`files/dot_loading` and `files/lottie_circle_loader`) and optionally tint
15
+ /// the fill/stroke. The tint is reproduced here by masking a solid color rect
16
+ /// with the (monochrome) Lottie animation.
17
+ public struct Loader: View {
18
+ private let type: LoaderType
19
+ private let tintColor: Color?
20
+
21
+ public init(type: LoaderType = .dot, tintColor: Color? = nil) {
22
+ self.type = type
23
+ self.tintColor = tintColor
24
+ }
25
+
26
+ public var body: some View {
27
+ switch type {
28
+ case .dot:
29
+ DotLoader(tintColor: tintColor)
30
+ case .spinner:
31
+ SpinnerLoader(tintColor: tintColor)
32
+ }
33
+ }
34
+ }
35
+
36
+ /// Looping dot animation. Compose size: 52 x 18.
37
+ public struct DotLoader: View {
38
+ private let tintColor: Color?
39
+
40
+ public init(tintColor: Color? = nil) {
41
+ self.tintColor = tintColor
42
+ }
43
+
44
+ public var body: some View {
45
+ TintedLottie(name: "dot_loading", tintColor: tintColor)
46
+ .frame(width: 52, height: 18)
47
+ }
48
+ }
49
+
50
+ /// Looping circular spinner. Compose size: 24 x 24.
51
+ public struct SpinnerLoader: View {
52
+ private let tintColor: Color?
53
+
54
+ public init(tintColor: Color? = nil) {
55
+ self.tintColor = tintColor
56
+ }
57
+
58
+ public var body: some View {
59
+ TintedLottie(name: "lottie_circle_loader", tintColor: tintColor)
60
+ .frame(width: 24, height: 24)
61
+ }
62
+ }
63
+
64
+ /// Horizontal determinate progress bar.
65
+ ///
66
+ /// Mirrors Compose `ProgressBar(percent:, color:, modifier:)`:
67
+ /// - `percent` clamped to 0...100, animated with a 200ms ease.
68
+ /// - track = theme background default (`0xFFF2F2F6`), fill = `color` or primary (pink_03).
69
+ /// - height 4, corner radius `Radius.XXS`, fills max width.
70
+ public struct ProgressBar: View {
71
+ @Environment(\.appTheme) private var theme
72
+
73
+ private let percent: CGFloat
74
+ private let color: Color?
75
+
76
+ public init(percent: CGFloat = 0, color: Color? = nil) {
77
+ self.percent = percent
78
+ self.color = color
79
+ }
80
+
81
+ private var clampedFraction: CGFloat {
82
+ min(max(percent, 0), 100) / 100
83
+ }
84
+
85
+ public var body: some View {
86
+ GeometryReader { proxy in
87
+ ZStack(alignment: .leading) {
88
+ RoundedRectangle(cornerRadius: Radius.XXS)
89
+ .fill(theme.colors.background.default)
90
+ RoundedRectangle(cornerRadius: Radius.XXS)
91
+ .fill(color ?? theme.colors.primary)
92
+ .frame(width: proxy.size.width * clampedFraction)
93
+ }
94
+ }
95
+ .frame(height: 4)
96
+ .animation(.easeInOut(duration: 0.2), value: clampedFraction)
97
+ }
98
+ }
99
+
100
+ /// Renders a looping Lottie animation, optionally recolored via a mask.
101
+ private struct TintedLottie: View {
102
+ let name: String
103
+ let tintColor: Color?
104
+
105
+ var body: some View {
106
+ if let tintColor = tintColor {
107
+ tintColor
108
+ .mask(LottieView(name: name, loopMode: .loop))
109
+ } else {
110
+ LottieView(name: name, loopMode: .loop)
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,86 @@
1
+ //
2
+ // LottieView.swift
3
+ // MoMoPlatform
4
+ //
5
+ // Created by thanhdat on 16/01/2023.
6
+ // Copyright © 2023 Facebook. All rights reserved.
7
+ //
8
+
9
+ import SwiftUI
10
+ import Lottie
11
+
12
+ struct LottieView: UIViewRepresentable {
13
+ var name: String = ""
14
+ var url: String?
15
+ var loopMode: LottieLoopMode = .playOnce
16
+ var contentMode : UIView.ContentMode = .scaleAspectFit
17
+ var onDone: (() -> Void)?
18
+
19
+ // Add static cache for animations
20
+ private static var animationCache = NSCache<NSString, LottieAnimation>()
21
+
22
+ func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
23
+ let view = UIView(frame: .zero)
24
+
25
+ // Configure Lottie with optimized settings
26
+ let configuration = LottieConfiguration(
27
+ renderingEngine: .coreAnimation,
28
+ decodingStrategy: .dictionaryBased
29
+ )
30
+
31
+ let animationView = LottieAnimationView(configuration: configuration)
32
+
33
+ if let url = url {
34
+ // Use cached animation if available
35
+ if let cachedAnimation = Self.animationCache.object(forKey: url as NSString) {
36
+ animationView.animation = cachedAnimation
37
+ setupAnimationView(animationView, view)
38
+ } else {
39
+ LottieAnimation.loadedFrom(url: URL(string: url)!, closure: { animation in
40
+ if let animation = animation {
41
+ Self.animationCache.setObject(animation, forKey: url as NSString)
42
+ animationView.animation = animation
43
+ setupAnimationView(animationView, view)
44
+ }
45
+ }, animationCache: nil)
46
+ }
47
+ } else {
48
+ // Use cached animation if available
49
+ if let cachedAnimation = Self.animationCache.object(forKey: name as NSString) {
50
+ animationView.animation = cachedAnimation
51
+ setupAnimationView(animationView, view)
52
+ } else if let animation = LottieAnimation.named(name) {
53
+ Self.animationCache.setObject(animation, forKey: name as NSString)
54
+ animationView.animation = animation
55
+ setupAnimationView(animationView, view)
56
+ }
57
+ }
58
+
59
+ return view
60
+ }
61
+
62
+ private func setupAnimationView(_ animationView: LottieAnimationView, _ containerView: UIView) {
63
+ animationView.contentMode = contentMode
64
+ animationView.loopMode = loopMode
65
+ animationView.backgroundBehavior = loopMode == .loop ? .pauseAndRestore : .pause
66
+
67
+ // Optimize rendering
68
+ animationView.shouldRasterizeWhenIdle = true
69
+
70
+ animationView.translatesAutoresizingMaskIntoConstraints = false
71
+ containerView.addSubview(animationView)
72
+
73
+ NSLayoutConstraint.activate([
74
+ animationView.widthAnchor.constraint(equalTo: containerView.widthAnchor),
75
+ animationView.heightAnchor.constraint(equalTo: containerView.heightAnchor)
76
+ ])
77
+
78
+ animationView.play { finished in
79
+ if finished {
80
+ onDone?()
81
+ }
82
+ }
83
+ }
84
+
85
+ func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {}
86
+ }
@@ -0,0 +1,5 @@
1
+ eval(
2
+ File.read(File.join(__dir__, 'native-kits.podspec')),
3
+ binding,
4
+ File.join(__dir__, 'native-kits.podspec')
5
+ )
@@ -8,9 +8,13 @@ typealias SwiftButton = SwiftUI.Button
8
8
  public struct KeyboardButton: View {
9
9
  // MARK: Lifecycle
10
10
 
11
- public init(_ key: String, action: @escaping (_ key: String)->Void) {
11
+ public init(_ key: String, action: @escaping (_ key: String)->Void, width: CGFloat = .infinity, height: CGFloat = 48, color: Color = Colors.card, textColor: Color = Colors.text) {
12
12
  self.key = key
13
13
  self.action = action
14
+ self.width = width
15
+ self.height = height
16
+ self.color = color
17
+ self.textColor = textColor
14
18
  }
15
19
 
16
20
  // MARK: Public
@@ -18,10 +22,10 @@ public struct KeyboardButton: View {
18
22
  public var body: some View {
19
23
  SwiftUI.Button(action: { self.action(key) }) {
20
24
  HStack {
21
- Text(key).font(.system(size: 24, weight: .bold))
22
- }.frame(maxWidth: .infinity, minHeight: 48)
23
- .background(Colors.card)
24
- .foregroundColor(Colors.text)
25
+ Text(key).font(.system(size: 24, weight: .bold)).ignoreBoldTextSetting()
26
+ }.frame(maxWidth: CGFloat(width), minHeight: height)
27
+ .background(color)
28
+ .foregroundColor(textColor)
25
29
  .clipShape(RoundedRectangle(cornerRadius: 8))
26
30
  }
27
31
  }
@@ -30,4 +34,8 @@ public struct KeyboardButton: View {
30
34
 
31
35
  var key: String = ""
32
36
  var action: (_ key: String)->Void
37
+ var width: CGFloat
38
+ var height: CGFloat
39
+ var color: Color
40
+ var textColor: Color
33
41
  }
@@ -119,7 +119,7 @@ public struct OTPKeyboard: View {
119
119
  private struct ValueView: View {
120
120
  // MARK: Lifecycle
121
121
 
122
- public init(_ value: String, valueLength: Int) {
122
+ init(_ value: String, valueLength: Int) {
123
123
  self.value = value
124
124
  self.valueLength = valueLength
125
125
  }
@@ -0,0 +1,61 @@
1
+ //
2
+ // PaginationDot.swift
3
+ // MoMoUIKits
4
+ //
5
+ // Native SwiftUI port of Compose's PaginationDot.
6
+ // An active dot is a 12x4 pill in `activeColor`; inactive dots are 4x4
7
+ // rounded squares in the pressed-background token.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ // MARK: - Dot
13
+
14
+ /// A single pagination dot, mirroring Compose's `Dot` composable.
15
+ public struct PaginationDotItem: View {
16
+ @Environment(\.appTheme) private var theme
17
+
18
+ private let active: Bool
19
+ private let activeColor: Color
20
+
21
+ public init(active: Bool = false, activeColor: Color) {
22
+ self.active = active
23
+ self.activeColor = activeColor
24
+ }
25
+
26
+ public var body: some View {
27
+ if active {
28
+ RoundedRectangle(cornerRadius: 4)
29
+ .fill(activeColor)
30
+ .frame(width: 12, height: 4)
31
+ } else {
32
+ RoundedRectangle(cornerRadius: 4)
33
+ .fill(theme.colors.background.pressed)
34
+ .frame(width: 4, height: 4)
35
+ }
36
+ }
37
+ }
38
+
39
+ // MARK: - PaginationDot
40
+
41
+ /// Mirrors Compose's `PaginationDot(activeIndex, dataLength)`.
42
+ public struct PaginationDot: View {
43
+ @Environment(\.appTheme) private var theme
44
+
45
+ private let activeIndex: Int
46
+ private let dataLength: Int
47
+
48
+ public init(activeIndex: Int = 0, dataLength: Int = 3) {
49
+ self.activeIndex = activeIndex
50
+ self.dataLength = dataLength
51
+ }
52
+
53
+ public var body: some View {
54
+ HStack(spacing: 0) {
55
+ ForEach(0..<max(dataLength, 0), id: \.self) { i in
56
+ PaginationDotItem(active: i == activeIndex, activeColor: theme.colors.primary)
57
+ .padding(.trailing, i != dataLength - 1 ? Spacing.XS : 0)
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,35 @@
1
+ //
2
+ // PaginationNumber.swift
3
+ // MoMoUIKits
4
+ //
5
+ // Native SwiftUI port of Compose's PaginationNumber.
6
+ // Renders "current/total" text on a translucent black rounded chip.
7
+ //
8
+
9
+ import SwiftUI
10
+
11
+ // MARK: - PaginationNumber
12
+
13
+ /// Mirrors Compose's `PaginationNumber(activeIndex, dataLength)`.
14
+ public struct PaginationNumber: View {
15
+ private let activeIndex: Int
16
+ private let dataLength: Int
17
+
18
+ public init(activeIndex: Int = 0, dataLength: Int = 2) {
19
+ self.activeIndex = activeIndex
20
+ self.dataLength = dataLength
21
+ }
22
+
23
+ public var body: some View {
24
+ MomoText(
25
+ "\(1 + activeIndex)/\(dataLength)",
26
+ typography: .labelDefaultMedium,
27
+ color: Colors.black01
28
+ )
29
+ .padding(.horizontal, Spacing.S)
30
+ .background(
31
+ RoundedRectangle(cornerRadius: Radius.L)
32
+ .fill(Color.black.opacity(0.2)) // Color(0x33000000)
33
+ )
34
+ }
35
+ }
@@ -0,0 +1,101 @@
1
+ //
2
+ // PaginationScroll.swift
3
+ // MoMoUIKits
4
+ //
5
+ // Native SwiftUI port of Compose's PaginationScroll.
6
+ // Wraps arbitrary horizontally-scrollable content and renders a
7
+ // progress-bar style indicator whose position tracks the scroll offset.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ // MARK: - Constants
13
+
14
+ private let INDICATOR_WIDTH: CGFloat = 24
15
+ private let PROGRESS_WIDTH: CGFloat = 72
16
+
17
+ /// Memoized offset calculation, mirroring Compose's `calculateOffset`.
18
+ private func calculateOffset(currentPosition: CGFloat, maxBound: CGFloat) -> CGFloat {
19
+ guard maxBound > 0 else { return 0 }
20
+ return (PROGRESS_WIDTH - INDICATOR_WIDTH) * (currentPosition / maxBound)
21
+ }
22
+
23
+ // MARK: - PaginationScroll
24
+
25
+ /// Mirrors Compose's `PaginationScroll(modifier, content)`.
26
+ ///
27
+ /// The trailing-closure `content` is laid out in a horizontal scroll view and
28
+ /// the indicator below reflects the current scroll progress.
29
+ public struct PaginationScroll<Content: View>: View {
30
+ @Environment(\.appTheme) private var theme
31
+
32
+ private let content: Content
33
+
34
+ @State private var scrollOffset: CGFloat = 0
35
+ @State private var contentWidth: CGFloat = 0
36
+ @State private var viewportWidth: CGFloat = 0
37
+
38
+ public init(@ViewBuilder content: () -> Content) {
39
+ self.content = content()
40
+ }
41
+
42
+ private var maxBound: CGFloat {
43
+ max(contentWidth - viewportWidth, 0)
44
+ }
45
+
46
+ private var indicatorOffset: CGFloat {
47
+ calculateOffset(currentPosition: scrollOffset, maxBound: maxBound)
48
+ }
49
+
50
+ public var body: some View {
51
+ VStack(spacing: 0) {
52
+ ScrollView(.horizontal, showsIndicators: false) {
53
+ HStack(spacing: 0) {
54
+ content
55
+ }
56
+ .background(
57
+ GeometryReader { proxy in
58
+ Color.clear
59
+ .preference(
60
+ key: ScrollOffsetKey.self,
61
+ value: -proxy.frame(in: .named("paginationScroll")).minX
62
+ )
63
+ .onAppear { contentWidth = proxy.size.width }
64
+ .onChange(of: proxy.size.width) { contentWidth = $0 }
65
+ }
66
+ )
67
+ }
68
+ .coordinateSpace(name: "paginationScroll")
69
+ .background(
70
+ GeometryReader { viewport in
71
+ Color.clear
72
+ .onAppear { viewportWidth = viewport.size.width }
73
+ .onChange(of: viewport.size.width) { viewportWidth = $0 }
74
+ }
75
+ )
76
+ .onPreferenceChange(ScrollOffsetKey.self) { scrollOffset = max($0, 0) }
77
+ .padding(.bottom, Spacing.L)
78
+
79
+ // Progress indicator
80
+ ZStack(alignment: .leading) {
81
+ RoundedRectangle(cornerRadius: Spacing.XS)
82
+ .fill(theme.colors.background.pressed)
83
+ .frame(width: PROGRESS_WIDTH, height: 4)
84
+
85
+ RoundedRectangle(cornerRadius: Spacing.XS)
86
+ .fill(theme.colors.primary)
87
+ .frame(width: INDICATOR_WIDTH, height: 4)
88
+ .offset(x: indicatorOffset)
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ // MARK: - Preference key
95
+
96
+ private struct ScrollOffsetKey: PreferenceKey {
97
+ static var defaultValue: CGFloat = 0
98
+ static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
99
+ value = nextValue()
100
+ }
101
+ }
@@ -0,0 +1,37 @@
1
+ //
2
+ // PaginationWhiteDot.swift
3
+ // MoMoUIKits
4
+ //
5
+ // Native SwiftUI port of Compose's PaginationWhiteDot.
6
+ // Same dot layout as PaginationDot, but the active color is white
7
+ // (black_01) and the row sits on a translucent black rounded surface.
8
+ //
9
+
10
+ import SwiftUI
11
+
12
+ // MARK: - PaginationWhiteDot
13
+
14
+ /// Mirrors Compose's `PaginationWhiteDot(activeIndex, dataLength)`.
15
+ public struct PaginationWhiteDot: View {
16
+ private let activeIndex: Int
17
+ private let dataLength: Int
18
+
19
+ public init(activeIndex: Int = 0, dataLength: Int = 3) {
20
+ self.activeIndex = activeIndex
21
+ self.dataLength = dataLength
22
+ }
23
+
24
+ public var body: some View {
25
+ HStack(spacing: 0) {
26
+ ForEach(0..<max(dataLength, 0), id: \.self) { i in
27
+ PaginationDotItem(active: i == activeIndex, activeColor: Colors.black01)
28
+ .padding(.trailing, i != dataLength - 1 ? Spacing.XS : 0)
29
+ }
30
+ }
31
+ .padding(Spacing.XS)
32
+ .background(
33
+ RoundedRectangle(cornerRadius: Radius.S)
34
+ .fill(Color.black.opacity(0.2)) // Color(0x33000000)
35
+ )
36
+ }
37
+ }