@momo-kits/native-kits 0.167.1-duo.8-debug → 0.167.1-duo.9

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 (254) hide show
  1. package/CLAUDE.md +4 -1
  2. package/ios/Application/ApplicationEnvironment.swift +11 -0
  3. package/ios/Application/ComposeApi.swift +2 -1
  4. package/ios/Application/{Components.swift → LegacyHeader.swift} +6 -62
  5. package/ios/Application/Screen.swift +3 -2
  6. package/ios/Avatar/Avatar.swift +4 -3
  7. package/ios/Badge/Badge.swift +6 -4
  8. package/ios/Badge/BadgeDot.swift +1 -1
  9. package/ios/Badge/BadgeRibbon.swift +0 -1
  10. package/ios/Button/Button.swift +15 -12
  11. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +12 -20
  12. package/ios/Carousel/Carousel.swift +29 -12
  13. package/ios/Checkbox/Checkbox.swift +7 -3
  14. package/ios/Chip/Chip.swift +5 -4
  15. package/ios/Collapse/Collapse.swift +70 -19
  16. package/ios/Colors+Radius+Spacing/Colors.swift +9 -0
  17. package/ios/DateTimePicker/DateTimePicker.swift +54 -20
  18. package/ios/DateTimePicker/WheelPicker.swift +103 -105
  19. package/ios/Divider/Divider.swift +37 -4
  20. package/ios/Extensions/Color++.swift +18 -0
  21. package/ios/Icon/Icon.swift +7 -1
  22. package/ios/IconButton/IconButton.swift +15 -7
  23. package/ios/Image/Image.swift +7 -34
  24. package/ios/Information/Information.swift +27 -9
  25. package/ios/Input/ErrorView.swift +2 -2
  26. package/ios/Input/Input.swift +160 -147
  27. package/ios/Input/InputPhoneNumber.swift +12 -9
  28. package/ios/Input/InputSearch.swift +102 -75
  29. package/ios/Input/InputTextArea.swift +128 -145
  30. package/ios/InputDropDown/InputDropDown.swift +10 -7
  31. package/ios/InputMoney/InputMoney.swift +33 -11
  32. package/ios/InputOTP/InputOTP.swift +22 -12
  33. package/ios/Layout/Card.swift +50 -0
  34. package/ios/Layout/GridContext.swift +123 -3
  35. package/ios/Layout/Item.swift +5 -4
  36. package/ios/Layout/Section.swift +4 -100
  37. package/ios/Loader/Loader.swift +5 -9
  38. package/ios/Lottie/LottieView.swift +10 -0
  39. package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +61 -9
  40. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTab.swift +60 -34
  41. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +11 -6
  42. package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
  43. package/ios/Navigation/Component/Header.swift +493 -0
  44. package/ios/Navigation/Component/HeaderBackground.swift +134 -0
  45. package/ios/Navigation/Component/HeaderJourney.swift +73 -0
  46. package/ios/Navigation/Component/HeaderLocation.swift +48 -0
  47. package/ios/Navigation/Component/HeaderRight.swift +386 -0
  48. package/ios/Navigation/Component/HeaderTitle.swift +36 -0
  49. package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +20 -23
  50. package/ios/{Application/Navigation/component → Navigation/Component}/SideBar.swift +49 -28
  51. package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
  52. package/ios/Navigation/HostBackGestureBridge.swift +57 -0
  53. package/ios/Navigation/Navigation.swift +190 -0
  54. package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +46 -41
  55. package/ios/{Application/Navigation → Navigation}/Navigator.swift +65 -9
  56. package/ios/{Application → Navigation}/StackScreen.swift +236 -103
  57. package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
  58. package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
  59. package/ios/Pagination/PaginationScroll.swift +2 -2
  60. package/ios/Popup/PopupDisplay.swift +79 -159
  61. package/ios/Popup/PopupInput.swift +10 -13
  62. package/ios/Popup/PopupLayout.swift +97 -2
  63. package/ios/Popup/PopupNotify.swift +27 -55
  64. package/ios/Popup/PopupPromotion.swift +22 -50
  65. package/ios/PopupView/FullscreenPopup.swift +13 -19
  66. package/ios/PopupView/PopupView.swift +1 -1
  67. package/ios/PopupView/Utils++.swift +0 -8
  68. package/ios/ProgressInfo/ProgressInfo.swift +4 -2
  69. package/ios/Radio/Radio.swift +2 -1
  70. package/ios/Rating/Rating.swift +9 -1
  71. package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
  72. package/ios/Skeleton/Skeleton.swift +22 -27
  73. package/ios/Slider/Slider.swift +16 -18
  74. package/ios/Stepper/Stepper.swift +6 -1
  75. package/ios/Steps/Steps.swift +8 -6
  76. package/ios/SuggestAction/SuggestAction.swift +5 -0
  77. package/ios/Swipeable/SwipeCell.swift +10 -6
  78. package/ios/Switch/Switch.swift +22 -20
  79. package/ios/TabView/TabView.swift +102 -21
  80. package/ios/Tag/Tag.swift +15 -4
  81. package/ios/Template/Logo/Logo.swift +2 -1
  82. package/ios/Template/TrustBanner/TrustBanner.swift +41 -27
  83. package/ios/Title/Title.swift +6 -17
  84. package/ios/Tooltip/Tooltip.swift +29 -14
  85. package/ios/Typography/Text.swift +61 -19
  86. package/ios/Uploader/Uploader.swift +3 -1
  87. package/ios/native-kits.podspec +1 -0
  88. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
  89. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
  90. package/package.json +1 -1
  91. package/build.gradle.kts +0 -11
  92. package/compose/build.gradle.kts +0 -204
  93. package/compose/build.gradle.kts.backup +0 -204
  94. package/compose/compose.podspec +0 -47
  95. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  96. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  97. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  98. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  99. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
  100. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -831
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -266
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -264
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  140. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  141. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  142. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  143. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  144. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  145. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  146. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  147. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  148. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
  149. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  150. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  151. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  152. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  153. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
  154. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  155. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  156. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  157. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
  158. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  159. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -552
  160. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
  161. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  162. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  163. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
  164. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  165. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  166. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  167. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  168. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
  169. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
  170. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  171. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  172. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  173. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  174. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  175. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  176. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  177. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -297
  178. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  179. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  180. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -232
  181. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  182. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  183. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  184. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  185. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  186. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  187. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  188. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  189. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  190. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  191. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
  192. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
  193. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
  194. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  195. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -708
  196. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  197. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  198. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -203
  199. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -232
  200. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  201. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -98
  202. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -312
  203. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  204. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -408
  205. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -92
  206. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -105
  207. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  208. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -366
  209. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -37
  210. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  211. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -185
  212. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
  213. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  214. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  215. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  216. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  217. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  218. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
  219. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  220. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  221. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  222. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  223. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  224. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  225. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  226. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  227. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  228. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  229. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -86
  230. package/gradle/libs.versions.toml +0 -69
  231. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  232. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  233. package/gradle.properties +0 -26
  234. package/gradlew +0 -252
  235. package/gradlew.bat +0 -94
  236. package/ios/Application/HeaderRight.swift +0 -324
  237. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  238. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  239. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  240. package/ios/Application/Navigation/NavigatorManager.swift +0 -29
  241. package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -54
  242. package/ios/Application/Navigation/component/Header.swift +0 -183
  243. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -84
  244. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  245. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
  246. package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
  247. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -280
  248. package/ios-demo/README.md +0 -120
  249. package/settings.gradle.kts +0 -64
  250. /package/ios/{Application/Layout → Layout}/ScreenLayout.swift +0 -0
  251. /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
  252. /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoBarCompression.swift +0 -0
  253. /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoTabBar.swift +0 -0
  254. /package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +0 -0
@@ -0,0 +1,48 @@
1
+ //
2
+ // HeaderLocation.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `navigation/component/HeaderLocation.kt`: a description above a
6
+ // tappable location with a chevron.
7
+ //
8
+
9
+ import SwiftUI
10
+
11
+ public struct HeaderLocationView: View {
12
+ private let data: HeaderLocationData
13
+ private let tintColor: Color
14
+
15
+ public init(_ data: HeaderLocationData, tintColor: Color) {
16
+ self.data = data
17
+ self.tintColor = tintColor
18
+ }
19
+
20
+ @Environment(\.screenLayout) private var layout
21
+
22
+ public var body: some View {
23
+ if data.isLoading {
24
+ VStack(alignment: .leading, spacing: Spacing.XXS) {
25
+ titleSkeleton(height: 12)
26
+ titleSkeleton(height: 18)
27
+ }
28
+ .frame(maxWidth: .infinity, alignment: .leading)
29
+ } else {
30
+ let tint = parseHeaderTint(data.tintColor) ?? tintColor
31
+ SwiftUI.Button(action: { data.onPress?() }) {
32
+ VStack(alignment: .leading, spacing: 0) {
33
+ if let description = data.description, !description.isEmpty {
34
+ MomoText(description, typography: .descriptionXsRegular, color: tint, maxLines: 1)
35
+ }
36
+ HStack(spacing: 0) {
37
+ MomoText(data.location, typography: .actionXsBold, color: tint, maxLines: 1)
38
+ Spacer().frame(width: Spacing.XS)
39
+ Icon(source: "16_arrow_chevron_down_small", size: 16, color: tint)
40
+ }
41
+ }
42
+ .frame(maxWidth: headerTitleMaxWidth(layout), alignment: .leading)
43
+ }
44
+ .buttonStyle(ActiveOpacityButtonStyle())
45
+ .disabled(data.onPress == nil)
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,386 @@
1
+ //
2
+ // HeaderRight.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `navigation/component/HeaderRight.kt`: the mini-app toolkit
6
+ // (shortcut / more / single tool, help center and close) in the bar or the iPhone Duo side bar.
7
+ //
8
+
9
+ import SwiftUI
10
+
11
+ /// Mirrors Compose's `HeaderRight.Toolkit`.
12
+ public struct HeaderRightData {
13
+ var useShortcut: Bool
14
+ var useMore: Bool
15
+ var tools: [ToolGroup]
16
+ var toolCallback: ((String) -> Void)?
17
+ var useSystemTools: Bool
18
+ /// Close dismisses only this mini app instead of returning home.
19
+ var useCloseIcon: Bool
20
+
21
+ public init(
22
+ useShortcut: Bool = false,
23
+ useMore: Bool = false,
24
+ tools: [ToolGroup] = [],
25
+ toolCallback: ((String) -> Void)? = nil,
26
+ useSystemTools: Bool = true,
27
+ useCloseIcon: Bool = false
28
+ ) {
29
+ self.useShortcut = useShortcut
30
+ self.useMore = useMore
31
+ self.tools = tools
32
+ self.toolCallback = toolCallback
33
+ self.useSystemTools = useSystemTools
34
+ self.useCloseIcon = useCloseIcon
35
+ }
36
+
37
+ var closeIcon: String { useCloseIcon ? "16_navigation_close_circle" : "16_basic_home" }
38
+
39
+ var showBadge: Bool { tools.contains { group in group.items.contains { $0.showBadge } } }
40
+ }
41
+
42
+ public struct Tool {
43
+ var key: String
44
+ var icon: String
45
+ var showBadge: Bool
46
+ var name: [String: String]
47
+ var showRightIcon: Bool
48
+
49
+ public init(
50
+ key: String,
51
+ icon: String,
52
+ name: [String: String] = [:],
53
+ showBadge: Bool = false,
54
+ showRightIcon: Bool = true
55
+ ) {
56
+ self.key = key
57
+ self.icon = icon
58
+ self.showBadge = showBadge
59
+ self.name = name
60
+ self.showRightIcon = showRightIcon
61
+ }
62
+
63
+ func toMap() -> [String: Any] {
64
+ [
65
+ "key": key,
66
+ "icon": icon,
67
+ "showBadge": showBadge,
68
+ "name": name,
69
+ "showRightIcon": showRightIcon
70
+ ]
71
+ }
72
+ }
73
+
74
+ public struct ToolGroup {
75
+ var title: [String: String]
76
+ var items: [Tool]
77
+
78
+ public init(title: [String: String] = [:], items: [Tool]) {
79
+ self.title = title
80
+ self.items = items
81
+ }
82
+
83
+ func toMap() -> [String: Any] {
84
+ [
85
+ "title": title,
86
+ "items": items.map { $0.toMap() }
87
+ ]
88
+ }
89
+ }
90
+
91
+ private let helpCenterAppId = "vn.momo.helpcenter"
92
+
93
+ /// The Help Center mini app hides its own help button; without a context there is nothing to open.
94
+ private func usesHelpCenter(_ context: MiniAppContext?) -> Bool {
95
+ guard let context else { return false }
96
+ return context.appId != helpCenterAppId
97
+ }
98
+
99
+ private func showsShortcut(_ data: HeaderRightData, _ context: MiniAppContext?) -> Bool {
100
+ data.useShortcut && !(data.useMore && context == nil)
101
+ }
102
+
103
+ /// Height of the vertical toolkit: the optional shortcut, then the help/home capsule.
104
+ func toolkitColumnHeight(_ data: HeaderRightData, context: MiniAppContext?) -> CGFloat {
105
+ let shortcut = showsShortcut(data, context) ? DuoMetrics.controlSize + Spacing.M : 0
106
+ return shortcut + (usesHelpCenter(context) ? 2 * DuoMetrics.itemPitch : DuoMetrics.controlSize)
107
+ }
108
+
109
+ /// A header right that renders the kit toolkit, so the Duo side bar can take it over.
110
+ protocol ToolkitHeaderRightView {
111
+ var headerRight: HeaderRightData? { get }
112
+ var tintColor: Color? { get }
113
+ }
114
+
115
+ /// Toolkit data when `headerRight` renders the kit toolkit; nil for a custom header right.
116
+ func toolkitHeaderRight(_ headerRight: (() -> any View)?) -> (data: HeaderRightData?, tintColor: Color?)? {
117
+ guard let toolkit = headerRight?() as? ToolkitHeaderRightView else { return nil }
118
+ return (toolkit.headerRight, toolkit.tintColor)
119
+ }
120
+
121
+ // MARK: - Public views
122
+
123
+ /// The kit toolkit. Works under `NavigationContainer` (keyed environment) and under hosts that
124
+ /// inject `ApplicationEnvironment` with `.environmentObject`.
125
+ public struct HeaderRight: View, ToolkitHeaderRightView {
126
+ var headerRight: HeaderRightData?
127
+ var tintColor: Color?
128
+
129
+ public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
130
+ self.headerRight = headerRight
131
+ self.tintColor = tintColor
132
+ }
133
+
134
+ public var body: some View {
135
+ Toolkit(headerRight: headerRight ?? HeaderRightData(), tintColor: tintColor)
136
+ }
137
+ }
138
+
139
+ @available(*, deprecated, renamed: "HeaderRight", message: "HeaderRight now reads both environment styles.")
140
+ public struct NavigationHeaderRight: View, ToolkitHeaderRightView {
141
+ var headerRight: HeaderRightData?
142
+ var tintColor: Color?
143
+
144
+ public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
145
+ self.headerRight = headerRight
146
+ self.tintColor = tintColor
147
+ }
148
+
149
+ public var body: some View {
150
+ Toolkit(headerRight: headerRight ?? HeaderRightData(), tintColor: tintColor)
151
+ }
152
+ }
153
+
154
+ // MARK: - Toolkit
155
+
156
+ private struct NavigationButtonConfig {
157
+ let icon: String
158
+ let onPress: () -> Void
159
+ }
160
+
161
+ struct Toolkit: View {
162
+ let headerRight: HeaderRightData
163
+ var tintColor: Color?
164
+ /// iPhone Duo side bar column instead of the bar row.
165
+ var vertical: Bool = false
166
+
167
+ @State private var isFavorite = false
168
+ @State private var isLoading = false
169
+ @OptionalEnvironmentObject private var injectedEnvironment: ApplicationEnvironment?
170
+ @Environment(\.applicationEnvironment) private var keyedEnvironment
171
+ @Environment(\.headerColor) private var barColor
172
+
173
+ private var environment: ApplicationEnvironment { injectedEnvironment ?? keyedEnvironment }
174
+ private var context: MiniAppContext? { environment.applicationContext }
175
+
176
+ /// The bar's colors when inside a header, otherwise derived from the tint like a plain header.
177
+ private var headerColor: HeaderColor {
178
+ barColor ?? getHeaderColor(isAnimated: false, isSurface: true, opacity: 1, tintColor: tintColor)
179
+ }
180
+
181
+ private func apiValue(_ value: Any?) -> Any { value ?? NSNull() }
182
+
183
+ private var contextMap: [String: Any] {
184
+ [
185
+ "appId": apiValue(context?.appId),
186
+ "code": apiValue(context?.appCode),
187
+ "name": apiValue(context?.appName),
188
+ "icon": apiValue(context?.appIcon),
189
+ "description": apiValue(context?.description),
190
+ "support": apiValue(context?.support),
191
+ "toolkitConfig": apiValue(context?.toolkitConfig),
192
+ "providerId": apiValue(context?.providerId),
193
+ "permissions": apiValue(context?.permissions)
194
+ ]
195
+ }
196
+
197
+ private func parseResponse(_ response: String) -> Any? {
198
+ guard let data = response.data(using: .utf8),
199
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
200
+ return nil
201
+ }
202
+ return json["response"]
203
+ }
204
+
205
+ // MARK: Actions
206
+
207
+ private func loadFavoriteState() {
208
+ _ = environment.composeApi?.request(
209
+ funcName: "isFavoriteApp",
210
+ params: ["code": apiValue(context?.appCode)]
211
+ ) { response in
212
+ isFavorite = parseResponse(response) as? Bool == true
213
+ }
214
+ }
215
+
216
+ private func onPressShortcut() {
217
+ _ = environment.composeApi?.request(
218
+ funcName: "onToolAction",
219
+ params: ["item": ["key": "onFavorite"], "context": contextMap]
220
+ ) { response in
221
+ if let result = parseResponse(response) as? [String: Any], result["success"] as? Bool == true {
222
+ isFavorite.toggle()
223
+ }
224
+ }
225
+ }
226
+
227
+ private func onPressMore() {
228
+ _ = environment.composeApi?.request(
229
+ funcName: "showTools",
230
+ params: [
231
+ "useSystemTools": headerRight.useSystemTools,
232
+ "tools": headerRight.tools.map { $0.toMap() },
233
+ "context": contextMap
234
+ ]
235
+ ) { response in
236
+ if let tool = parseResponse(response) as? String {
237
+ headerRight.toolCallback?(tool)
238
+ }
239
+ }
240
+ }
241
+
242
+ private func onPressHelpCenter() {
243
+ guard context != nil else { return }
244
+ _ = environment.composeApi?.request(
245
+ funcName: "showHelpCenter",
246
+ params: [
247
+ "appId": apiValue(context?.appId),
248
+ "code": apiValue(context?.appCode),
249
+ "name": apiValue(context?.appName),
250
+ "icon": apiValue(context?.appIcon),
251
+ "description": apiValue(context?.description)
252
+ ]
253
+ ) { _ in }
254
+ }
255
+
256
+ private func onPressClose() {
257
+ if headerRight.useCloseIcon {
258
+ environment.requestDismiss()
259
+ } else {
260
+ _ = environment.composeApi?.request(funcName: "dismissAll", params: nil) { _ in }
261
+ }
262
+ }
263
+
264
+ private var navigationButtonConfig: NavigationButtonConfig {
265
+ let totalTools = headerRight.tools.reduce(0) { $0 + $1.items.count }
266
+ if totalTools > 1 || headerRight.useMore {
267
+ return NavigationButtonConfig(icon: "navigation_more_icon", onPress: onPressMore)
268
+ }
269
+ if totalTools == 1, let tool = headerRight.tools.first?.items.first {
270
+ return NavigationButtonConfig(icon: tool.icon) { headerRight.toolCallback?(tool.key) }
271
+ }
272
+ return NavigationButtonConfig(icon: isFavorite ? "pin_star_checked" : "pin_star", onPress: onPressShortcut)
273
+ }
274
+
275
+ // MARK: Body
276
+
277
+ var body: some View {
278
+ Group {
279
+ if vertical {
280
+ verticalBody
281
+ } else {
282
+ horizontalBody
283
+ }
284
+ }
285
+ .onAppear(perform: loadFavoriteState)
286
+ }
287
+
288
+ private var verticalBody: some View {
289
+ let config = navigationButtonConfig
290
+ let useHelpCenter = usesHelpCenter(context)
291
+ return VStack(spacing: Spacing.M) {
292
+ if showsShortcut(headerRight, context) {
293
+ SideBarButton(
294
+ icon: config.icon,
295
+ headerColor: headerColor,
296
+ showBadge: headerRight.showBadge,
297
+ disabled: isLoading,
298
+ action: config.onPress
299
+ )
300
+ }
301
+ SideBarGroup(headerColor: headerColor, itemCount: useHelpCenter ? 2 : 1) {
302
+ if useHelpCenter {
303
+ SideBarGroupItem(
304
+ icon: "help_center",
305
+ headerColor: headerColor,
306
+ accessibilityId: "btn_navigation_help_center",
307
+ action: onPressHelpCenter
308
+ )
309
+ }
310
+ SideBarGroupItem(
311
+ icon: headerRight.closeIcon,
312
+ headerColor: headerColor,
313
+ accessibilityId: "btn_navigation_close",
314
+ action: onPressClose
315
+ )
316
+ }
317
+ }
318
+ }
319
+
320
+ private var horizontalBody: some View {
321
+ let config = navigationButtonConfig
322
+ return HStack(alignment: .center, spacing: 0) {
323
+ if showsShortcut(headerRight, context) {
324
+ NavigationButton(
325
+ disabled: isLoading,
326
+ icon: config.icon,
327
+ showBadge: headerRight.showBadge,
328
+ headerColor: headerColor,
329
+ onClick: config.onPress
330
+ )
331
+ }
332
+
333
+ HStack(alignment: .center, spacing: 0) {
334
+ if usesHelpCenter(context) {
335
+ toolButton(icon: "help_center", accessibilityId: "btn_navigation_help_center", action: onPressHelpCenter)
336
+ Rectangle()
337
+ .fill(tintColor ?? Colors.black20)
338
+ .frame(width: 0.5, height: 12)
339
+ }
340
+ toolButton(icon: headerRight.closeIcon, accessibilityId: "btn_navigation_close", action: onPressClose)
341
+ }
342
+ .frame(width: 65, height: 28)
343
+ .background(headerColor.backgroundButton)
344
+ .cornerRadius(14)
345
+ .overlay(RoundedRectangle(cornerRadius: 14).stroke(headerColor.borderColor, lineWidth: 0.2))
346
+ .padding(.leading, Spacing.S)
347
+ }
348
+ }
349
+
350
+ private func toolButton(icon: String, accessibilityId: String, action: @escaping () -> Void) -> some View {
351
+ SwiftUI.Button(action: action) {
352
+ Icon(source: icon, size: 20, color: headerColor.tintIconColor)
353
+ .padding(4)
354
+ }
355
+ .buttonStyle(ActiveOpacityButtonStyle())
356
+ .accessibilityIdentifier(accessibilityId)
357
+ }
358
+ }
359
+
360
+ /// Mirrors Compose's `NavigationButton`: the 28pt circular shortcut / more button.
361
+ struct NavigationButton: View {
362
+ var disabled: Bool
363
+ var icon: String
364
+ var showBadge: Bool
365
+ var headerColor: HeaderColor
366
+ var onClick: () -> Void
367
+
368
+ var body: some View {
369
+ SwiftUI.Button(action: onClick) {
370
+ ZStack {
371
+ Circle()
372
+ .fill(headerColor.backgroundButton)
373
+ .overlay(Circle().stroke(headerColor.borderColor, lineWidth: 0.2))
374
+ Icon(source: icon, size: 20, color: headerColor.tintIconColor)
375
+ }
376
+ .frame(width: 28, height: 28)
377
+ .overlay(alignment: .topTrailing) {
378
+ if showBadge {
379
+ BadgeDot(size: .small).offset(x: -Spacing.XXS, y: -Spacing.XXS)
380
+ }
381
+ }
382
+ }
383
+ .buttonStyle(ActiveOpacityButtonStyle())
384
+ .disabled(disabled)
385
+ }
386
+ }
@@ -0,0 +1,36 @@
1
+ //
2
+ // HeaderTitle.swift
3
+ // MoMoUIKits
4
+ //
5
+ // SwiftUI mirror of Compose's `navigation/component/HeaderTitle.kt`: the plain header title.
6
+ //
7
+
8
+ import SwiftUI
9
+
10
+ /// `actionSBold` at 15/22 in the header family, single line.
11
+ struct HeaderTitleText: View {
12
+ let title: String
13
+ let color: Color
14
+ var centered: Bool = false
15
+
16
+ init(_ title: String, color: Color, centered: Bool = false) {
17
+ self.title = title
18
+ self.color = color
19
+ self.centered = centered
20
+ }
21
+
22
+ var body: some View {
23
+ MomoText(
24
+ title,
25
+ typography: .actionSBold,
26
+ color: color,
27
+ textAlign: centered ? .center : .leading,
28
+ maxLines: 1,
29
+ truncationMode: .tail,
30
+ accessibilityId: "title_navigation_header",
31
+ fontFamily: headerFontFamily
32
+ )
33
+ .fontSize(15, lineHeight: 22)
34
+ .frame(maxWidth: centered ? .infinity : nil, alignment: centered ? .center : .leading)
35
+ }
36
+ }
@@ -2,7 +2,7 @@
2
2
  // HeaderUser.swift
3
3
  // MoMoUIKits
4
4
  //
5
- // SwiftUI mirror of Compose's `HeaderUser` (navigation/component/HeaderUser.kt).
5
+ // SwiftUI mirror of Compose's `navigation/component/HeaderUser.kt`.
6
6
  // Renders a `HeaderTitle.User`: an avatar group (1..n avatars with overflow),
7
7
  // a title with optional inline icons, and an optional subtitle. Shows a
8
8
  // skeleton shimmer placeholder while `isLoading`.
@@ -21,11 +21,6 @@ public struct HeaderUserView: View {
21
21
 
22
22
  @Environment(\.screenLayout) private var layout
23
23
 
24
- // back button and toolkit share the bar unless the iPhone Duo side bar holds them
25
- private var maxWidth: CGFloat {
26
- layout.sideBar != nil ? layout.contentWidth - Spacing.M * 2 : layout.width - 172
27
- }
28
-
29
24
  public var body: some View {
30
25
  if data.isLoading {
31
26
  shimmer
@@ -35,6 +30,14 @@ public struct HeaderUserView: View {
35
30
  }
36
31
 
37
32
  private var content: some View {
33
+ SwiftUI.Button(action: { data.onPress?() }) {
34
+ row
35
+ }
36
+ .buttonStyle(ActiveOpacityButtonStyle())
37
+ .disabled(data.onPress == nil)
38
+ }
39
+
40
+ private var row: some View {
38
41
  HStack(spacing: 0) {
39
42
  HStack(spacing: 0) {
40
43
  ZStack(alignment: .bottomTrailing) {
@@ -51,8 +54,7 @@ public struct HeaderUserView: View {
51
54
 
52
55
  VStack(alignment: .leading, spacing: 0) {
53
56
  HStack(spacing: 0) {
54
- MomoText(data.title, typography: .actionXsBold, color: data.tintColor ?? Colors.black17, maxLines: 1)
55
- .truncationMode(.tail)
57
+ MomoText(data.title, typography: .actionXsBold, color: data.tintColor, maxLines: 1, fontFamily: headerFontFamily)
56
58
 
57
59
  if !data.icons.isEmpty {
58
60
  Spacer().frame(width: 6)
@@ -68,34 +70,27 @@ public struct HeaderUserView: View {
68
70
 
69
71
  if let subTitle = data.subTitle, !subTitle.isEmpty {
70
72
  Spacer().frame(height: 2)
71
- MomoText(subTitle, typography: .descriptionXsRegular, color: data.tintColor ?? Colors.black17, maxLines: 1)
72
- .truncationMode(.tail)
73
+ MomoText(subTitle, typography: .descriptionXsRegular, color: data.tintColor, maxLines: 1)
73
74
  }
74
75
  }
75
76
  }
76
- .frame(maxWidth: maxWidth, alignment: .leading)
77
+ .frame(maxWidth: headerTitleMaxWidth(layout), alignment: .leading)
77
78
 
78
79
  Spacer(minLength: 0)
79
80
  }
80
81
  .frame(maxWidth: .infinity, alignment: .leading)
81
82
  .padding(.vertical, Spacing.XS)
82
83
  .contentShape(Rectangle())
83
- .onTapGesture { data.onPress?() }
84
- .allowsHitTesting(data.onPress != nil)
85
84
  }
86
85
 
87
86
  private var shimmer: some View {
88
87
  HStack(spacing: Spacing.M / 2) {
89
- Circle()
90
- .fill(Colors.black05)
88
+ Skeleton()
91
89
  .frame(width: avatarSize, height: avatarSize)
90
+ .clipShape(Circle())
92
91
  VStack(alignment: .leading, spacing: Spacing.XXS) {
93
- RoundedRectangle(cornerRadius: 8)
94
- .fill(Colors.black05)
95
- .frame(width: 120, height: 18)
96
- RoundedRectangle(cornerRadius: 8)
97
- .fill(Colors.black05)
98
- .frame(width: 120, height: 12)
92
+ titleSkeleton(height: 18)
93
+ titleSkeleton(height: 12)
99
94
  }
100
95
  }
101
96
  .frame(maxWidth: .infinity, alignment: .leading)
@@ -108,6 +103,7 @@ public struct HeaderUserView: View {
108
103
  /// Mirrors Compose's `AvatarGroup`: lays out 1..4 avatars in a fixed collage,
109
104
  /// falls back to initials when empty, and shows a "+n" overflow tile beyond 4.
110
105
  private struct AvatarGroup: View {
106
+ @Environment(\.appTheme) private var theme
111
107
  let urls: [String]
112
108
  let title: String
113
109
  let size: CGFloat
@@ -177,7 +173,7 @@ private struct AvatarGroup: View {
177
173
  ImageView(url)
178
174
  .aspectRatio(contentMode: .fill)
179
175
  .clipShape(Circle())
180
- .overlay(Circle().stroke(Colors.black05, lineWidth: 0.5))
176
+ .overlay(Circle().stroke(theme.colors.border.default, lineWidth: 0.5))
181
177
  }
182
178
 
183
179
  private func overflowTile(more: Int) -> some View {
@@ -189,6 +185,7 @@ private struct AvatarGroup: View {
189
185
  }
190
186
 
191
187
  private struct InitialsAvatar: View {
188
+ @Environment(\.appTheme) private var theme
192
189
  let name: String
193
190
  let size: CGFloat
194
191
 
@@ -204,6 +201,6 @@ private struct InitialsAvatar: View {
204
201
  .frame(width: size, height: size)
205
202
  .background(Colors.pink09)
206
203
  .clipShape(Circle())
207
- .overlay(Circle().stroke(Colors.black05, lineWidth: 0.5))
204
+ .overlay(Circle().stroke(theme.colors.border.default, lineWidth: 0.5))
208
205
  }
209
206
  }