@momo-kits/native-kits 0.163.1-beta.2 → 0.163.1-beta.20

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 (203) hide show
  1. package/.claude/settings.local.json +112 -0
  2. package/CLAUDE.md +1 -1
  3. package/ios/Application/ApplicationEnvironment.swift +15 -3
  4. package/ios/Application/Components.swift +36 -30
  5. package/ios/Application/FloatingButton.swift +10 -9
  6. package/ios/Application/Localize.swift +309 -0
  7. package/ios/Application/MaxApi.swift +18 -0
  8. package/ios/Application/Navigation/BottomTab/BottomTab.swift +104 -0
  9. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  10. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  11. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  12. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  13. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  14. package/ios/Application/Navigation/NavigationContainer.swift +203 -0
  15. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  16. package/ios/Application/Navigation/Navigator.swift +385 -0
  17. package/ios/Application/Navigation/Overplay/BottomSheet.swift +256 -0
  18. package/ios/Application/Navigation/Overplay/ModalScreen.swift +74 -0
  19. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  20. package/ios/Application/Navigation/Tracking/ScreenTracker.swift +117 -0
  21. package/ios/Application/Navigation/component/Header.swift +178 -0
  22. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  23. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  24. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +11 -0
  25. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +196 -0
  26. package/ios/Application/Screen.swift +2 -1
  27. package/ios/Application/StackScreen.swift +501 -0
  28. package/ios/Avatar/Avatar.swift +193 -0
  29. package/ios/Badge/Badge.swift +11 -14
  30. package/ios/Badge/BadgeRibbon.swift +91 -17
  31. package/ios/BaselineView/BaselineView.swift +163 -0
  32. package/ios/Button/Button.swift +137 -28
  33. package/ios/Carousel/Carousel.swift +196 -0
  34. package/ios/Checkbox/Checkbox.swift +23 -28
  35. package/ios/Chip/Chip.swift +56 -33
  36. package/ios/Collapse/Collapse.swift +192 -0
  37. package/ios/Colors+Radius+Spacing/Colors.swift +20 -17
  38. package/ios/Colors+Radius+Spacing/Spacing.swift +3 -1
  39. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  40. package/ios/DateTimePicker/DateTimePicker.swift +212 -0
  41. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  42. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  43. package/ios/DateTimePicker/WheelPicker.swift +165 -0
  44. package/ios/Divider/Divider.swift +16 -0
  45. package/ios/Extensions/ActiveOpacityButtonStyle.swift +14 -0
  46. package/ios/Extensions/Color++.swift +32 -0
  47. package/ios/Icon/Icon.swift +10 -1
  48. package/ios/IconButton/IconButton.swift +140 -0
  49. package/ios/Image/Image.swift +16 -3
  50. package/ios/Information/Information.swift +3 -1
  51. package/ios/Input/ErrorView.swift +3 -1
  52. package/ios/Input/Input.swift +72 -33
  53. package/ios/Input/InputPhoneNumber.swift +33 -21
  54. package/ios/Input/InputSearch.swift +107 -48
  55. package/ios/Input/InputTextArea.swift +134 -64
  56. package/ios/InputDropDown/InputDropDown.swift +201 -0
  57. package/ios/InputMoney/InputMoney.swift +347 -0
  58. package/ios/InputOTP/InputOTP.swift +210 -0
  59. package/ios/Loader/Loader.swift +113 -0
  60. package/ios/MoMoUIKits.podspec +5 -0
  61. package/ios/Pagination/PaginationDot.swift +61 -0
  62. package/ios/Pagination/PaginationNumber.swift +35 -0
  63. package/ios/Pagination/PaginationScroll.swift +101 -0
  64. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  65. package/ios/Popup/PopupDisplay.swift +11 -20
  66. package/ios/Popup/PopupInput.swift +2 -8
  67. package/ios/Popup/PopupNotify.swift +218 -0
  68. package/ios/Popup/PopupPromotion.swift +5 -3
  69. package/ios/ProgressInfo/ProgressInfo.swift +296 -0
  70. package/ios/Radio/Radio.swift +69 -0
  71. package/ios/Rating/Rating.swift +82 -0
  72. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +11 -0
  73. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +38 -0
  74. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +38 -0
  75. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +38 -0
  76. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +6 -0
  77. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +38 -0
  78. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +38 -0
  79. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +38 -0
  80. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +38 -0
  81. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +38 -0
  82. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +38 -0
  83. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +38 -0
  84. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +38 -0
  85. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
  86. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
  87. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
  88. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
  89. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
  90. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
  91. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
  92. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
  93. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
  94. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
  95. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
  96. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
  97. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
  98. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
  99. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
  100. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
  101. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
  102. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
  103. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
  104. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +38 -0
  105. package/ios/Skeleton/Skeleton.swift +99 -0
  106. package/ios/Slider/Slider.swift +237 -0
  107. package/ios/Stepper/Stepper.swift +220 -0
  108. package/ios/Steps/Steps.swift +467 -0
  109. package/ios/SuggestAction/SuggestAction.swift +99 -0
  110. package/ios/Swipeable/SwipeCell.swift +187 -0
  111. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  112. package/ios/Switch/Switch.swift +53 -28
  113. package/ios/TabView/TabView.swift +534 -0
  114. package/ios/Tag/Tag.swift +98 -0
  115. package/ios/Template/TrustBanner/TrustBanner.swift +7 -8
  116. package/ios/Title/Title.swift +269 -0
  117. package/ios/Tooltip/Tooltip.swift +485 -0
  118. package/ios/Typography/FontScaleStore.swift +19 -0
  119. package/ios/Typography/Text.swift +86 -17
  120. package/ios/Typography/Typography.swift +12 -14
  121. package/ios/Uploader/Uploader.swift +174 -0
  122. package/ios/native-kits.podspec +61 -8
  123. package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
  124. package/ios-demo/Resources/dot_loading.json +558 -0
  125. package/ios-demo/Resources/icon.json +4052 -0
  126. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  127. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  128. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  129. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  130. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  131. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  132. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  133. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  134. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  135. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  136. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  137. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
  138. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  139. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  140. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  141. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  142. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  143. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  144. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  145. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +64 -0
  146. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  147. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  148. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  149. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  150. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  151. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  152. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +279 -0
  153. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  154. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  155. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  156. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  157. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  158. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  159. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  160. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  161. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  162. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  163. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  164. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +188 -0
  165. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +229 -0
  166. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  167. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  168. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  169. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  170. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  171. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  172. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  173. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  176. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  177. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  178. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  179. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  180. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  181. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  182. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  183. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  184. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  185. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  186. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  187. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  188. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +51 -0
  189. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  190. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  191. package/package.json +1 -1
  192. package/publish.sh +53 -0
  193. package/scripts/gen-ios-color.mjs +136 -0
  194. package/example/ios/Example.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  195. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  196. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  197. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  198. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  199. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  200. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  201. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  202. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -46
  203. package/ios/Application/FontScaleStore.swift +0 -59
@@ -0,0 +1,112 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(curl -s -o /dev/null -w \"%{http_code}\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
5
+ "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/maven-metadata.xml\")",
6
+ "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/maven-metadata.xml\")",
7
+ "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/0.144.6/skiko-0.144.6.module\")",
8
+ "Bash(curl -s -o /dev/null -w '%{http_code}' https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.__TRACKED_VAR__)",
9
+ "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/\")",
10
+ "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/\")",
11
+ "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.148.1/skiko-android-0.148.1.pom\")",
12
+ "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.5/skiko-android-0.144.5.pom\")",
13
+ "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://dl.google.com/dl/android/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
14
+ "Bash(curl -s \"https://search.maven.org/solrsearch/select?q=a:skiko-android&rows=5&wt=json\")",
15
+ "Bash(curl -sL -o /dev/null -w \"%{http_code} %{url_effective}\\\\n\" \"https://repo1.maven.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
16
+ "Bash(curl *)",
17
+ "Bash(./gradlew :sample:androidApp:dependencies --configuration debugRuntimeClasspath)",
18
+ "Bash(./gradlew :sample:shared:compileDebugKotlinAndroid)",
19
+ "Bash(./gradlew :sample:androidApp:compileDebugKotlin)",
20
+ "Bash(ls -d *.xcodeproj)",
21
+ "Bash(grep -c ChipDemo.swift __TRACKED_VAR__/project.pbxproj)",
22
+ "Bash(grep -c PBXFileSystemSynchronizedRootGroup __TRACKED_VAR__/project.pbxproj)",
23
+ "Bash(grep -c InformationDemo.swift __TRACKED_VAR__/project.pbxproj)",
24
+ "Bash(plutil -lint project.pbxproj)",
25
+ "Bash(xcode-select -p)",
26
+ "Bash(/usr/bin/xcrun xcodebuild *)",
27
+ "Bash(echo \"exit: $?\")",
28
+ "Bash(/Applications/Xcode26.app/Contents/Developer/usr/bin/xcodebuild -version)",
29
+ "Bash(./gradlew :compose:tasks --all)",
30
+ "Bash(grep -n \"vanniktech\\\\|mavenPublishing\\\\|^publishing\\\\|version = gitlabVersion\\\\|namespace =\\\\|compileSdk =\\\\|minSdk =\\\\|GitLabPackages\\\\|name = \\\\\"\\\\${gitlabRepo}Packages\\\\\"\")",
31
+ "Bash(git checkout *)",
32
+ "Bash(./gradlew :compose:publishAllPublicationsToGitLabPackagesRepository --dry-run)",
33
+ "Bash(git fetch *)",
34
+ "Bash(git pull *)",
35
+ "Bash(git push *)",
36
+ "Bash(git branch *)",
37
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -list)",
38
+ "Bash(xcrun simctl *)",
39
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
40
+ "Bash(pod install *)",
41
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'id=1E6F9A79-5E64-4590-BEA2-828D75D99393' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
42
+ "Bash(xcrun swiftc *)",
43
+ "Bash(echo \"=== exit: $? \\(1=none found\\) ===\")",
44
+ "Bash(cat Radius.swift)",
45
+ "Bash(cat Spacing.swift)",
46
+ "Bash(echo \"exit=$?\")",
47
+ "Bash(ruby *)",
48
+ "Bash(xcodebuild -list -workspace iosApp.xcworkspace)",
49
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' build)",
50
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
51
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
52
+ "Bash(xcodebuild build *)",
53
+ "Bash(git stash *)",
54
+ "Bash(plutil -lint sample/iosApp/iosApp.xcodeproj/project.pbxproj)",
55
+ "Bash(git add *)",
56
+ "Bash(git commit -m 'feat: DS-646 [ci build] *)",
57
+ "Bash(git commit -q -m 'fix: DS-656 [ci build] *)",
58
+ "Bash(xargs git log --oneline -1)",
59
+ "Bash(git rev-list *)",
60
+ "Bash(git status *)",
61
+ "Bash(git rebase *)",
62
+ "Bash(git merge *)",
63
+ "Bash(git reset *)",
64
+ "Bash(awk '/^ *)",
65
+ "Bash(git ls-tree *)",
66
+ "Bash(awk *)",
67
+ "Read(//Users/sophia/Workspace/momo/momo-app/**)",
68
+ "Bash(git log *)",
69
+ "Bash(xcodebuild -version)",
70
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
71
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
72
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug clean build CODE_SIGNING_ALLOWED=NO)",
73
+ "Bash(git commit *)",
74
+ "Bash(npm view *)",
75
+ "Bash(echo \"EXIT=$? -> not resolvable\")",
76
+ "Bash(yarn install *)",
77
+ "Bash(RCT_NEW_ARCH_ENABLED=1 pod install)",
78
+ "Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -list)",
79
+ "Bash(git --no-pager diff --stat ios/Application/Localize.swift ios/Application/Navigation/NavigationContainer.swift ios/Popup/PopupDisplay.swift ios/Popup/PopupNotify.swift ios/Template/TrustBanner/TrustBanner.swift)",
80
+ "Bash(git rm *)",
81
+ "Bash(echo \"EXIT=$?\")",
82
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff sample/iosApp/iosApp/Demo/ButtonDemo.swift)",
83
+ "Bash(python3 *)",
84
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits status --short)",
85
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits log --oneline -3)",
86
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits log --oneline -5 -- ios/Button/Button.swift)",
87
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff ios/native-kits.podspec compose/compose.podspec gradle.properties package.json)",
88
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits show HEAD:ios/Button/Button.swift)",
89
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff package.json ios/native-kits.podspec)",
90
+ "Bash(grep -n \"must be a \\\\`.background\\\\` of the label\" /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Button/Button.swift)",
91
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits show -s --format=\"%h %ci %s\" 953b234d)",
92
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-app show -s --format=\"%h %ci %s\" f8757a14437)",
93
+ "Bash(git -C /Users/sophia/Workspace/momo/momo-app log -S '\"@momo-kits/native-kits\": \"0.163.1-beta.18\"' --format=\"%h %ci %s\" -- package.json)",
94
+ "Bash(swiftc -O repro_isfull.swift -o repro_isfull)",
95
+ "Bash(./repro_isfull)",
96
+ "Bash(grep -n '^\\\\[32;1m$\\\\|section_start\\\\|xcodebuild.*-workspace\\\\|xcodebuild.*archive\\\\|Prebuilt\\\\|prebuilt\\\\|framework.*download\\\\|rugby\\\\|Rugby' /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/7e2b5bd7-f8b3-42f7-b242-127fa8eabd12/scratchpad/job_trace.log)",
97
+ "Bash(grep -n '\\\\[32;1m\\\\$' /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/7e2b5bd7-f8b3-42f7-b242-127fa8eabd12/scratchpad/job_trace.log)",
98
+ "Bash(npm pack *)",
99
+ "Bash(mkdir -p b13 b18)",
100
+ "Bash(tar xzf *)",
101
+ "Bash(git *)",
102
+ "Bash(tar -tzf momo-kits-native-kits-0.163.1-beta.18.tgz)",
103
+ "Bash(tar -xzf momo-kits-native-kits-0.163.1-beta.18.tgz package/ios/Button/Button.swift)",
104
+ "Bash(tar -xzf momo-kits-native-kits-0.162.28.tgz package/ios/Button/Button.swift -C .)",
105
+ "Bash(mkdir -p old)",
106
+ "Bash(tar -xzf momo-kits-native-kits-0.162.28.tgz -C old package/ios/Button/Button.swift)",
107
+ "Bash(nm '/private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/7e2b5bd7-f8b3-42f7-b242-127fa8eabd12/scratchpad/ipa_extract/Payload/MoMoPlatform UAT.app/MoMoPlatform UAT')",
108
+ "Bash(xcodebuild -workspace /Users/sophia/Workspace/momo/momo-native-kits/sample/iosApp/iosApp.xcworkspace -list)",
109
+ "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' build)"
110
+ ]
111
+ }
112
+ }
package/CLAUDE.md CHANGED
@@ -40,7 +40,7 @@ Publishing is automated (`publish.sh` + GitLab CI on `[ci build]` / `main`). Do
40
40
  ## Architecture essentials
41
41
 
42
42
  - **Entry point:** wrap content in `NavigationContainer(...)` from `navigation/`. It builds the `Navigator`, takes a host-supplied `Localize` (falls back to an empty one), and provides the CompositionLocals below. **Do not use** `ApplicationContainer` (`application/NavigationContainer.kt`) — deprecated and does **not** wire `LocalLocalize` / `LocalNavigator` / `LocalMaxApi`.
43
- - **Configuration flows via CompositionLocals**, not params. Public ones: `AppTheme`, `AppStatusBar`, `AppNavigationBar` (`const/Theme.kt`); `ApplicationContext`, `AppConfig`, `AppLanguage`, `LocalLocalize`, `ScaleSizeMaxRate` (`application/`); `LocalNavigator`, `LocalMaxApi` (`navigation/`). Read with `X.current`.
43
+ - **Configuration flows via CompositionLocals**, not params. Public ones: `AppTheme`, `AppStatusBar`, `AppNavigationBar` (`const/Theme.kt`); `ApplicationContext`, `AppConfig`, `AppLanguage`, `LocalLocalize` (`application/`); `LocalNavigator`, `LocalMaxApi` (`navigation/`). Read with `X.current`.
44
44
  - **Platform code** goes through `expect`/`actual` in `platform/` — never reference Android/iOS APIs from `commonMain` directly.
45
45
  - **Native bridge:** `IMaxApi` (`vn.momo.maxapi`, exposed as `LocalMaxApi`) is the host-app bridge (dismiss, tracking, device info, language).
46
46
  - Navigation uses a runtime `DynamicScreenRegistry`; overlays (modal/bottom-sheet/snackbar) go through `Navigator` + `OverplayComponentRegistry`.
@@ -41,11 +41,23 @@ public class ApplicationEnvironment: ObservableObject {
41
41
  let applicationContext: MiniAppContext?
42
42
  let composeApi: KitComposeApi?
43
43
  let config: KitConfig?
44
-
45
- public init(applicationContext: MiniAppContext? = nil, composeApi: KitComposeApi? = nil, config: KitConfig? = nil) {
44
+ let maxApi: MaxApi?
45
+
46
+ public init(applicationContext: MiniAppContext? = nil, composeApi: KitComposeApi? = nil, config: KitConfig? = nil, maxApi: MaxApi? = nil) {
46
47
  self.applicationContext = applicationContext
47
48
  self.composeApi = composeApi
48
49
  self.config = config
49
- FontScaleStore.shared.bind(composeApi)
50
+ self.maxApi = maxApi
51
+ }
52
+ }
53
+
54
+ private struct ApplicationEnvironmentKey: EnvironmentKey {
55
+ static let defaultValue = ApplicationEnvironment()
56
+ }
57
+
58
+ public extension EnvironmentValues {
59
+ var applicationEnvironment: ApplicationEnvironment {
60
+ get { self[ApplicationEnvironmentKey.self] }
61
+ set { self[ApplicationEnvironmentKey.self] = newValue }
50
62
  }
51
63
  }
@@ -83,9 +83,8 @@ public struct HeaderBackground: View {
83
83
  self.headerTransparent = headerTransparent
84
84
  self.fullScreenContent = fullScreenContent
85
85
  }
86
-
86
+
87
87
  public var body: some View {
88
-
89
88
  let statusBarHeight = safeAreaTopInset()
90
89
  let opacity = max(0, 1 - scrollState / 200)
91
90
  let height = fullScreenContent ? 0 : statusBarHeight + 52
@@ -115,7 +114,6 @@ public struct HeaderBackground: View {
115
114
  .opacity(opacity)
116
115
  .frame(height: height))
117
116
  )
118
-
119
117
  if !headerTransparent {
120
118
  Rectangle()
121
119
  .fill(Color.black.opacity(0.1))
@@ -148,12 +146,31 @@ public struct HeaderBackground: View {
148
146
  }
149
147
  }
150
148
 
149
+ // MARK: - Bottom-tab root preference
150
+
151
+ /// Set by `BottomTab` on appear so its enclosing `StackScreen` can detect that
152
+ /// it's hosting a self-contained bottom-tab surface and skip its own bottom
153
+ /// safe-area reservation — mirrors Compose's `Navigation.markAsBottomTabRoot()`
154
+ /// (`navigation.isBottomTabRoot`, read by `StackScreen.kt`'s `MainContent`).
155
+ /// Without this, `BottomTab`'s own internal safe-area handling gets an extra,
156
+ /// unwanted inset stacked on top of it by the outer `StackScreen`, pushing the
157
+ /// tab bar up higher than the true bottom inset.
158
+ public struct IsBottomTabRootKey: PreferenceKey {
159
+ public static var defaultValue: Bool = false
160
+ public static func reduce(value: inout Bool, nextValue: () -> Bool) {
161
+ value = value || nextValue()
162
+ }
163
+ }
164
+
151
165
  // MARK: - Header View
152
166
 
153
167
  public struct Header: View {
154
168
  var headerType: HeaderType = .default
155
169
  var titlePosition: TitlePosition
156
170
  var title: String
171
+ /// Optional rich title (mirrors Compose's `HeaderTitle`); `.user` renders an
172
+ /// avatar group instead of `title`. Defaults to nil → plain `title` text.
173
+ var headerTitle: HeaderTitle?
157
174
  var headerRight: (()->any View)? = {HeaderRight()}
158
175
  var goBack: (() -> Void)?
159
176
  var opacity: CGFloat = 1
@@ -161,22 +178,27 @@ public struct Header: View {
161
178
  var scrollState: CGFloat = 0
162
179
  var inputSearchProps: InputSearchProps?
163
180
  var tintColor: Color?
181
+ /// Title opacity; used by the animated search header to fade the title out on scroll.
182
+ var titleOpacity: CGFloat = 1
164
183
 
165
184
  public init(
166
185
  headerType: HeaderType = .default,
167
186
  titlePosition: TitlePosition,
168
187
  title: String,
188
+ headerTitle: HeaderTitle? = nil,
169
189
  headerRight: (()->any View)? = {HeaderRight()},
170
190
  goBack: (() -> Void)? = nil,
171
191
  opacity: CGFloat = 1,
172
192
  animatedHeader: AnimatedHeader? = nil,
173
193
  scrollState: CGFloat = 0,
174
194
  inputSearchProps: InputSearchProps? = nil,
175
- tintColor: Color? = nil
195
+ tintColor: Color? = nil,
196
+ titleOpacity: CGFloat = 1
176
197
  ) {
177
198
  self.headerType = headerType
178
199
  self.titlePosition = titlePosition
179
200
  self.title = title
201
+ self.headerTitle = headerTitle
180
202
  self.headerRight = headerRight
181
203
  self.goBack = goBack
182
204
  self.opacity = opacity
@@ -184,10 +206,11 @@ public struct Header: View {
184
206
  self.scrollState = scrollState
185
207
  self.inputSearchProps = inputSearchProps
186
208
  self.tintColor = tintColor
209
+ self.titleOpacity = titleOpacity
187
210
  }
188
-
211
+
189
212
  public var body: some View {
190
-
213
+
191
214
  let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
192
215
  let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
193
216
 
@@ -223,33 +246,16 @@ public struct Header: View {
223
246
  }
224
247
 
225
248
  if let inputProps = inputSearchProps {
226
- InputSearch(
227
- text: inputProps.$text,
228
- buttonText: inputProps.buttonText,
229
- showButtonText: inputProps.showButtonText,
230
- showBorder: inputProps.showBorder,
231
- placeholder: inputProps.placeholder,
232
- onChangeText: inputProps.onChangeText,
233
- onPressButtonText: inputProps.onPressButtonText,
234
- error: inputProps.error,
235
- disabled: inputProps.disabled,
236
- icon: inputProps.icon,
237
- iconColor: inputProps.iconColor,
238
- onRightIconPressed: inputProps.onRightIconPressed,
239
- onFocus: inputProps.onFocus,
240
- onBlur: inputProps.onBlur,
241
- loading: inputProps.loading,
242
- fontWeight: inputProps.fontWeight,
243
- keyboardType: inputProps.keyboardType
244
- )
249
+ InputSearch(props: inputProps)
250
+ } else if case let .user(userData)? = headerTitle {
251
+ if #available(iOS 16.0, *) {
252
+ HeaderUserView(userData)
253
+ }
245
254
  } else {
246
- Text(title)
247
- .font(.system(size: 17, weight: .bold))
248
- .ignoreBoldTextSetting()
249
- .foregroundColor(tintColor ?? Colors.black17)
255
+ MomoText(headerTitle?.defaultTitle ?? title, typography: .headerDefaultBold, color: tintColor ?? Colors.black17, maxLines: 1)
256
+ .opacity(titleOpacity)
250
257
  .frame(maxWidth: titlePosition == .center ? UIScreen.main.bounds.width - 252 : nil)
251
258
  .frame(maxWidth: titlePosition == .center ? .infinity : UIScreen.main.bounds.width - 172, alignment: titlePosition == .center ? .center : .leading)
252
- .lineLimit(1)
253
259
  }
254
260
 
255
261
  Spacer()
@@ -3,11 +3,16 @@ import Combine
3
3
 
4
4
  public enum FABSize {
5
5
  case small
6
+ case `default`
7
+ /// Backward-compatible alias for older iOS call sites. Compose names this
8
+ /// size `DEFAULT`.
6
9
  case large
7
-
8
10
  }
9
11
 
10
12
  public enum FABPosition {
13
+ case end
14
+ /// Backward-compatible alias for older iOS call sites. Compose names this
15
+ /// position `END`.
11
16
  case right
12
17
  case center
13
18
  }
@@ -31,10 +36,10 @@ public struct FabProps {
31
36
  label: String? = nil,
32
37
  onClick: (() -> Void)? = nil,
33
38
  containerColor: Color? = Colors.primary,
34
- size: FABSize? = .small,
39
+ size: FABSize? = .default,
35
40
  iconSize: CGFloat? = nil,
36
41
  scrollOffset: CGFloat? = nil,
37
- position: FABPosition? = .right,
42
+ position: FABPosition? = .end,
38
43
  bottomPadding: CGFloat? = nil,
39
44
  renderComponent: (() -> AnyView)? = nil
40
45
  ) {
@@ -65,7 +70,7 @@ public struct FloatingButton: View {
65
70
  private var keyboardOffset: CGFloat
66
71
  private var scrollOffset: CGFloat
67
72
  private var bottomPadding: CGFloat { props.bottomPadding ?? 12 + keyboardOffset}
68
- private var position: FABPosition? { props.position ?? .right }
73
+ private var position: FABPosition? { props.position ?? .end }
69
74
  private var containerColor: Color? { props.containerColor ?? Colors.primary }
70
75
  private var iconColor: Color? { props.iconColor ?? .white }
71
76
  private var label: String? { props.label }
@@ -152,11 +157,7 @@ public struct FloatingButton: View {
152
157
  if let label = label {
153
158
  if showText {
154
159
  Spacer().frame(width: 8)
155
- Text(label)
156
- .foregroundColor(.white)
157
- .font(.system(size: scaleSize(16), weight: .bold))
158
- .ignoreBoldTextSetting()
159
- .lineLimit(1)
160
+ MomoText(label, typography: .actionDefaultBold, color: .white, maxLines: 1)
160
161
  .background(
161
162
  GeometryReader { geo in
162
163
  Color.clear.onAppear {
@@ -0,0 +1,309 @@
1
+ //
2
+ // Localize.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `application/Localize.kt`, itself a port of
6
+ // momo-kits React Native's `Application/Localize.ts`. Holds the merged
7
+ // dictionary (kit defaults + host overrides) and the active language,
8
+ // switched at runtime via `changeLanguage`. `Localize` is an `ObservableObject`
9
+ // so a language switch republishes to consumers of `translate`/`translateData`,
10
+ // mirroring Compose's snapshot-state-backed `language`.
11
+ //
12
+
13
+ import Foundation
14
+ import SwiftUI
15
+
16
+ /// Translation dictionary keyed by language; both `vi` and `en` are required.
17
+ public struct LocalizationObject {
18
+ public var vi: [String: String]
19
+ public var en: [String: String]
20
+
21
+ public init(vi: [String: String] = [:], en: [String: String] = [:]) {
22
+ self.vi = vi
23
+ self.en = en
24
+ }
25
+ }
26
+
27
+ public final class Localize: ObservableObject {
28
+ public static let VI = "vi"
29
+ public static let EN = "en"
30
+
31
+ private var assets: LocalizationObject
32
+ @Published public private(set) var currentLanguage: String = Localize.VI
33
+
34
+ public init(_ translations: LocalizationObject = LocalizationObject()) {
35
+ self.assets = Localize.merge(base: Localize.defaultLanguage, override: translations)
36
+ }
37
+
38
+ public func changeLanguage(_ language: String?) {
39
+ currentLanguage = (language == Localize.EN) ? Localize.EN : Localize.VI
40
+ }
41
+
42
+ public func translate(_ key: String) -> String {
43
+ let dictionary = currentLanguage == Localize.EN ? assets.en : assets.vi
44
+ if let value = dictionary[key], !value.isEmpty { return value }
45
+ return key
46
+ }
47
+
48
+ public func translateData(_ data: [String: String]) -> String {
49
+ if let value = data[currentLanguage] { return value }
50
+ let body = data.map { "\"\($0.key)\":\"\($0.value)\"" }.joined(separator: ",")
51
+ return "{\(body)}"
52
+ }
53
+
54
+ public func translateData(vi: String, en: String) -> String {
55
+ currentLanguage == Localize.EN ? en : vi
56
+ }
57
+
58
+ /// Existing keys win (mirrors Compose: `merge(translations, assets)`).
59
+ public func addTranslations(_ translations: LocalizationObject) {
60
+ assets = Localize.merge(base: translations, override: assets)
61
+ }
62
+
63
+ private static func merge(base: LocalizationObject, override: LocalizationObject) -> LocalizationObject {
64
+ LocalizationObject(
65
+ vi: base.vi.merging(override.vi) { _, new in new },
66
+ en: base.en.merging(override.en) { _, new in new }
67
+ )
68
+ }
69
+
70
+ private static let defaultLanguage = LocalizationObject(
71
+ vi: [
72
+ "errorCode": "Mã lỗi: ",
73
+ "seeMore": "Xem thêm",
74
+ "cancel": "Huỷ",
75
+ "done": "Xong",
76
+ "confirm": "Xác nhận",
77
+ "month": "tháng",
78
+ "day": "ngày",
79
+ "choose": "Chọn",
80
+ "sent": "Đã gửi",
81
+ "received": "Đã tiếp nhận",
82
+ "processing": "Đang xử lý",
83
+ "processed": "Đã xử lý",
84
+ "addImg": "Thêm hình",
85
+ "chooseBtn": "Chọn button",
86
+ "imgEg": "Hình ảnh tham khảo",
87
+ "filter": "Sắp xếp",
88
+ "shipping": "Đang giao hàng",
89
+ "men": "Nam",
90
+ "women": "Nữ",
91
+ "more": "Khác",
92
+ "expDate": "Ngày hết hạn",
93
+ "exp": "HSD",
94
+ "inActive": "Chưa kích hoạt",
95
+ "voucherRemindHour": "Hết hạn sau {hours} giờ",
96
+ "voucherRemindMinute": "Hết hạn sau {minutes} phút",
97
+ "voucherRemindSecond": "Hết hạn sau {seconds} giây",
98
+ "voucherRemindDay": "Hết hạn sau {days} ngày",
99
+ "chooseRoundtrip": "Chọn vé khứ hồi",
100
+ "depart": "Ngày đi",
101
+ "return": "Ngày về",
102
+ "departing": "Đi",
103
+ "returning": "Về",
104
+ "jan": "Tháng 1",
105
+ "feb": "Tháng 2",
106
+ "mar": "Tháng 3",
107
+ "apr": "Tháng 4",
108
+ "may": "Tháng 5",
109
+ "jun": "Tháng 6",
110
+ "jul": "Tháng 7",
111
+ "aug": "Tháng 8",
112
+ "sep": "Tháng 9",
113
+ "oct": "Tháng 10",
114
+ "nov": "Tháng 11",
115
+ "dec": "Tháng 12",
116
+ "mon": "T2",
117
+ "tue": "T3",
118
+ "wed": "T4",
119
+ "thu": "T5",
120
+ "fri": "T6",
121
+ "sat": "T7",
122
+ "sun": "CN",
123
+ "showLunar": "Hiển thị lịch âm",
124
+ "newYear": "Tết Dương lịch",
125
+ "valentine": "Ngày Lễ Tình nhân",
126
+ "womenDay": "Ngày Quốc tế Phụ nữ",
127
+ "liberationDay": "Ngày giải phóng Miền Nam thống nhất đất nước",
128
+ "laborDay": "Ngày Quốc tế Lao động",
129
+ "childrenDay": "Ngày Quốc tế Thiếu nhi",
130
+ "nationalDay": "Quốc khánh nước CHXHCN Việt Nam",
131
+ "womenDayVN": "Ngày Phụ nữ Việt Nam",
132
+ "teacherDay": "Ngày Nhà giáo Việt Nam",
133
+ "christmasEve": "Ngày Lễ Giáng Sinh",
134
+ "christmas": "Ngày Lễ Giáng Sinh",
135
+ "lunarNewYear": "Tết Nguyên Đán",
136
+ "hungKingDay": "Giỗ Tổ Hùng Vương",
137
+ "balance": "Số dư trong ví",
138
+ "chooseDate": "Chọn ngày tháng năm",
139
+ "year": "năm",
140
+ "hour": "giờ",
141
+ "minute": "phút",
142
+ "from": "Từ",
143
+ "to": "Đến",
144
+ "connected": "Đã kết nối",
145
+ "disconnected": "Mất kết nối",
146
+ "hide": "Ẩn",
147
+ "viewAllTitle": "Xem tất cả",
148
+ "headerTitle": "Lịch sử thanh toán",
149
+ "SHORTEN": "THU GỌN",
150
+ "viewMoreBank": "XEM THÊM (${otherItemCount} ngân hàng)",
151
+ "transaction_success": "Thành công",
152
+ "transaction_fail": "Thất bại",
153
+ "transaction_processing": "Đang xử lý",
154
+ "viewMore": "Xem thêm",
155
+ "shorten": "Thu gọn",
156
+ "Payment": "Thanh toán ${serviceName}",
157
+ "currencyUnit": "đ",
158
+ "Month": "Tháng",
159
+ "save": "Lưu",
160
+ "favoriteIn": "Thêm dịch vụ yêu thích",
161
+ "favoriteOut": "Xóa dịch vụ yêu thích",
162
+ "deviceIn": "Thêm vào thiết bị",
163
+ "setting": "Cài đặt",
164
+ "transaction": "Lịch sử giao dịch",
165
+ "share": "Chia sẽ dịch vụ",
166
+ "information": "Thông tin chung",
167
+ "tutorial": "Hướng dẫn sử dụng",
168
+ "question": "Câu hỏi thường gặp",
169
+ "support": "Trung tâm hỗ trợ",
170
+ "skip": "Bỏ qua",
171
+ "enterPhoneNumber": "Vui lòng nhập số điện thoại",
172
+ "invalidPhoneNumber": "Số điện thoại không đúng",
173
+ ],
174
+ en: [
175
+ "seeMore": "See more",
176
+ "cancel": "Cancel",
177
+ "done": "Done",
178
+ "confirm": "Confirm",
179
+ "month": "month",
180
+ "day": "day",
181
+ "choose": "Choose",
182
+ "sent": "Sent",
183
+ "received": "Received",
184
+ "processing": "Processing",
185
+ "processed": "Done",
186
+ "addImg": "Add image",
187
+ "chooseBtn": "Choose button",
188
+ "imgEg": "Example images",
189
+ "filter": "Filter",
190
+ "shipping": "Shipping",
191
+ "men": "Men",
192
+ "women": "Women",
193
+ "more": "More",
194
+ "expDate": "Expiration date",
195
+ "exp": "EXP",
196
+ "inActive": "Not Activated",
197
+ "voucherRemindHour": "Revoke after {hours} hours",
198
+ "voucherRemindMinute": "Revoke after {minutes} minutes",
199
+ "voucherRemindSecond": "Revoke after {seconds} seconds",
200
+ "voucherRemindDay": "Revoke after {days} days",
201
+ "chooseRoundtrip": "Choose roundtrip ticket",
202
+ "depart": "Departing",
203
+ "return": "Returning",
204
+ "departing": "De",
205
+ "returning": "Re",
206
+ "jan": "January",
207
+ "feb": "February",
208
+ "mar": "March",
209
+ "apr": "April",
210
+ "may": "May",
211
+ "jun": "June",
212
+ "jul": "July",
213
+ "aug": "August",
214
+ "sep": "September",
215
+ "oct": "October",
216
+ "nov": "November",
217
+ "dec": "December",
218
+ "mon": "Mon",
219
+ "tue": "Tue",
220
+ "wed": "Wed",
221
+ "thu": "Thu",
222
+ "fri": "Fri",
223
+ "sat": "Sat",
224
+ "sun": "Sun",
225
+ "showLunar": "Show Lunar Calendar",
226
+ "newYear": "New Year's Day",
227
+ "valentine": "Valentine's Day",
228
+ "womenDay": "International Women's Day",
229
+ "liberationDay": "Liberation Day",
230
+ "laborDay": "Internation Labor's Day",
231
+ "childrenDay": "International Labor's Day",
232
+ "nationalDay": "National Day",
233
+ "womenDayVN": "Vietnamese Women's Day",
234
+ "teacherDay": "Teacher's Day",
235
+ "christmasEve": "Christmas Eve",
236
+ "christmas": "Christmas Day",
237
+ "lunarNewYear": "Lunar New Year",
238
+ "hungKingDay": "Hung Kings' Festival",
239
+ "balance": "Wallet Balance",
240
+ "chooseDate": "Choose date",
241
+ "year": "year",
242
+ "hour": "hour",
243
+ "minute": "minute",
244
+ "from": "From",
245
+ "to": "To",
246
+ "connected": "Connected",
247
+ "disconnected": "Disconnected",
248
+ "hide": "Hide",
249
+ "viewAllTitle": "View all",
250
+ "headerTitle": "Transaction history",
251
+ "SHORTEN": "SHORTEN",
252
+ "viewMoreBank": "VIEW MORE (${otherItemCount} banks)",
253
+ "transaction_success": "Success",
254
+ "transaction_fail": "Fail",
255
+ "transaction_processing": "Processing",
256
+ "viewMore": "View more",
257
+ "shorten": "Shorten",
258
+ "Payment": "${serviceName} payment",
259
+ "currencyUnit": "VND",
260
+ "Month": "Month",
261
+ "save": "Save",
262
+ "favoriteIn": "Add to favorite services",
263
+ "favoriteOut": "Remove to favorite services",
264
+ "deviceIn": "Add to device",
265
+ "setting": "Settings",
266
+ "transaction": "Transaction History",
267
+ "share": "Share",
268
+ "information": "Information",
269
+ "tutorial": "Instruction",
270
+ "question": "FAQ",
271
+ "support": "Support center",
272
+ "errorCode": "Error code: ",
273
+ "skip": "Skip",
274
+ "enterPhoneNumber": "Please enter your phone number",
275
+ "invalidPhoneNumber": "Invalid phone number",
276
+ ]
277
+ )
278
+ }
279
+
280
+ /// Snapshots `currentLanguage` alongside the instance so SwiftUI sees an environment
281
+ /// *change* on `changeLanguage`: the `Localize` reference alone never changes, and
282
+ /// `@Environment` does not subscribe to `@Published` the way `@EnvironmentObject` does.
283
+ private struct LocalizeBox: Equatable {
284
+ let localize: Localize
285
+ let language: String
286
+
287
+ init(_ localize: Localize) {
288
+ self.localize = localize
289
+ self.language = localize.currentLanguage
290
+ }
291
+
292
+ static func == (lhs: LocalizeBox, rhs: LocalizeBox) -> Bool {
293
+ lhs.localize === rhs.localize && lhs.language == rhs.language
294
+ }
295
+ }
296
+
297
+ private struct LocalizeKey: EnvironmentKey {
298
+ /// A default-constructed `Localize` already carries the full kit dictionary, so a
299
+ /// consumer that never injects one renders untranslated-but-correct text instead of
300
+ /// trapping the way `@EnvironmentObject` does.
301
+ static let defaultValue = LocalizeBox(Localize())
302
+ }
303
+
304
+ public extension EnvironmentValues {
305
+ var localize: Localize {
306
+ get { self[LocalizeKey.self].localize }
307
+ set { self[LocalizeKey.self] = LocalizeBox(newValue) }
308
+ }
309
+ }