@momo-kits/native-kits 0.300.3-debug → 0.300.4-debug

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/CLAUDE.md +51 -7
  2. package/README.md +2 -2
  3. package/compose/build.gradle.kts +10 -2
  4. package/compose/build.gradle.kts.backup +8 -0
  5. package/compose/compose.podspec +1 -3
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +4 -3
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -14
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +351 -111
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +1 -1
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +55 -22
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -1
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +13 -6
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +5 -2
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +2 -1
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +20 -15
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -2
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +306 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +10 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +6 -2
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +6 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +197 -60
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +132 -83
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +54 -62
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +97 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +377 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +122 -24
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +34 -14
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +113 -26
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +2 -2
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HelpCenterRequest.kt +37 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +192 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +8 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +20 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +13 -0
  65. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/HelpCenterRequestTest.kt +44 -0
  66. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +15 -0
  67. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +3 -1
  68. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +4 -0
  69. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +138 -0
  70. package/gradle/libs.versions.toml +3 -14
  71. package/gradle/wrapper/gradle-wrapper.properties +0 -2
  72. package/gradle.properties +3 -5
  73. package/ios/Application/ApplicationEnvironment.swift +24 -6
  74. package/ios/Application/DesignSystemKits.swift +100 -0
  75. package/ios/Application/DesignSystemKitsPayload.swift +68 -0
  76. package/ios/Application/MaxApi.swift +30 -3
  77. package/ios/Avatar/Avatar.swift +4 -3
  78. package/ios/Badge/Badge.swift +6 -4
  79. package/ios/Badge/BadgeDot.swift +1 -1
  80. package/ios/Badge/BadgeRibbon.swift +8 -2
  81. package/ios/Button/Button.swift +15 -12
  82. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
  83. package/ios/Carousel/Carousel.swift +29 -12
  84. package/ios/Checkbox/Checkbox.swift +7 -3
  85. package/ios/Chip/Chip.swift +5 -4
  86. package/ios/Collapse/Collapse.swift +70 -19
  87. package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
  88. package/ios/Colors+Radius+Spacing/Radius.swift +6 -0
  89. package/ios/DateTimePicker/DateTimePicker.swift +54 -20
  90. package/ios/DateTimePicker/WheelPicker.swift +103 -105
  91. package/ios/Divider/Divider.swift +37 -4
  92. package/ios/Extensions/Color++.swift +38 -9
  93. package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
  94. package/ios/Extensions/SkeletonUICompat.swift +11 -0
  95. package/ios/Icon/Icon.swift +21 -23
  96. package/ios/Icon/Icons.swift +1329 -0
  97. package/ios/IconButton/IconButton.swift +15 -7
  98. package/ios/Image/Image.swift +7 -23
  99. package/ios/Information/Information.swift +27 -9
  100. package/ios/Input/ErrorView.swift +2 -2
  101. package/ios/Input/Input.swift +171 -148
  102. package/ios/Input/InputPhoneNumber.swift +10 -9
  103. package/ios/Input/InputSearch.swift +107 -78
  104. package/ios/Input/InputTextArea.swift +133 -148
  105. package/ios/InputDropDown/InputDropDown.swift +10 -7
  106. package/ios/InputMoney/InputMoney.swift +32 -11
  107. package/ios/InputOTP/InputOTP.swift +22 -12
  108. package/ios/Layout/Card.swift +50 -0
  109. package/ios/Layout/GridContext.swift +187 -3
  110. package/ios/Layout/Item.swift +5 -4
  111. package/ios/Layout/ScreenLayout.swift +415 -0
  112. package/ios/Layout/Section.swift +4 -100
  113. package/ios/Loader/Loader.swift +5 -9
  114. package/ios/Lottie/LottieView.swift +14 -2
  115. package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +169 -29
  116. package/ios/Navigation/BottomTab/BottomTab.swift +332 -0
  117. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +18 -7
  118. package/ios/Navigation/BottomTab/DuoBarCompression.swift +104 -0
  119. package/ios/Navigation/BottomTab/DuoTabBar.swift +329 -0
  120. package/ios/{Application → Navigation/Component}/FloatingButton.swift +43 -54
  121. package/ios/Navigation/Component/Header.swift +545 -0
  122. package/ios/Navigation/Component/HeaderBackground.swift +154 -0
  123. package/ios/Navigation/Component/HeaderJourney.swift +73 -0
  124. package/ios/Navigation/Component/HeaderLocation.swift +48 -0
  125. package/ios/Navigation/Component/HeaderRight.swift +425 -0
  126. package/ios/Navigation/Component/HeaderTitle.swift +36 -0
  127. package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
  128. package/ios/Navigation/Component/SideBar.swift +183 -0
  129. package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +8 -8
  130. package/ios/Navigation/HostBackGestureBridge.swift +78 -0
  131. package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +9 -2
  132. package/ios/Navigation/Navigation.swift +204 -0
  133. package/ios/Navigation/NavigationContainer.swift +195 -0
  134. package/ios/{Application/Navigation → Navigation}/Navigator.swift +207 -107
  135. package/ios/Navigation/StackScreen.swift +930 -0
  136. package/ios/Navigation/StackTransition.swift +400 -0
  137. package/ios/Navigation/SwipeBackHaptic.swift +33 -0
  138. package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
  139. package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
  140. package/ios/Pagination/PaginationScroll.swift +2 -2
  141. package/ios/Popup/PopupDisplay.swift +72 -135
  142. package/ios/Popup/PopupInput.swift +10 -13
  143. package/ios/Popup/PopupLayout.swift +138 -0
  144. package/ios/Popup/PopupNotify.swift +59 -69
  145. package/ios/Popup/PopupPromotion.swift +31 -47
  146. package/ios/PopupView/FullscreenPopup.swift +13 -19
  147. package/ios/PopupView/PopupView.swift +1 -1
  148. package/ios/PopupView/Utils++.swift +0 -8
  149. package/ios/ProgressInfo/ProgressInfo.swift +4 -2
  150. package/ios/Radio/Radio.swift +2 -1
  151. package/ios/Rating/Rating.swift +9 -1
  152. package/ios/Resources/dot_loading.json +558 -0
  153. package/ios/Resources/lottie_circle_loader.json +1 -0
  154. package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
  155. package/ios/Skeleton/Skeleton.swift +22 -27
  156. package/ios/Slider/Slider.swift +16 -18
  157. package/ios/Stepper/Stepper.swift +7 -3
  158. package/ios/Steps/Steps.swift +8 -6
  159. package/ios/SuggestAction/SuggestAction.swift +5 -0
  160. package/ios/Swipeable/SwipeCell.swift +10 -6
  161. package/ios/Switch/Switch.swift +22 -20
  162. package/ios/TabView/TabView.swift +102 -21
  163. package/ios/Tag/Tag.swift +15 -4
  164. package/ios/Template/Logo/Logo.swift +2 -1
  165. package/ios/Template/TrustBanner/TrustBanner.swift +109 -55
  166. package/ios/Title/Title.swift +6 -17
  167. package/ios/Tooltip/Tooltip.swift +29 -14
  168. package/ios/Typography/FontScale.swift +17 -14
  169. package/ios/Typography/Text.swift +156 -20
  170. package/ios/Typography/Typography.swift +2 -2
  171. package/ios/Uploader/Uploader.swift +3 -1
  172. package/ios/native-kits.podspec +2 -3
  173. package/ios-demo/MoMoUIKitsDemo.podspec +1 -3
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
  176. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +2 -0
  177. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
  178. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
  179. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +1 -0
  180. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +2 -2
  181. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +2 -0
  182. package/package.json +1 -1
  183. package/publish.sh +19 -2
  184. package/scripts/gen-ios-icons.mjs +36 -0
  185. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  186. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  187. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  188. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  189. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  190. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  191. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  192. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  193. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  194. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  195. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  196. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  197. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  198. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  199. package/ios/Application/Components.swift +0 -270
  200. package/ios/Application/ComposeApi.swift +0 -22
  201. package/ios/Application/HeaderRight.swift +0 -306
  202. package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
  203. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  204. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  205. package/ios/Application/Navigation/NavigationContainer.swift +0 -213
  206. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  207. package/ios/Application/Navigation/NavigatorManager.swift +0 -22
  208. package/ios/Application/Navigation/component/Header.swift +0 -178
  209. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
  210. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  211. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
  212. package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
  213. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
  214. package/ios/Application/Screen.swift +0 -250
  215. package/ios/Application/StackScreen.swift +0 -492
  216. package/ios/Typography/FontScaleStore.swift +0 -58
  217. package/ios/Typography/KitDesignSystem.swift +0 -17
  218. /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
@@ -1,270 +0,0 @@
1
- //
2
- // Components.swift
3
- // Pods
4
- //
5
- // Created by sophia on 24/1/25.
6
- //
7
-
8
- import SwiftUI
9
-
10
- // MARK: - Enums
11
-
12
- public enum TitlePosition {
13
- case left, center
14
- }
15
-
16
- public enum HeaderType {
17
- case `default`, extended, none
18
- }
19
-
20
- public enum AnimatedHeaderRatio {
21
- case ratio16_9
22
- case ratio1_1
23
- case ratio3_2
24
-
25
- var value: CGFloat {
26
- switch self {
27
- case .ratio16_9: return 16.0 / 9.0
28
- case .ratio1_1: return 1.0
29
- case .ratio3_2: return 3.0 / 2.0
30
- }
31
- }
32
- }
33
-
34
-
35
- // MARK: - Animated Header Config
36
-
37
- public struct AnimatedHeader {
38
- public var aspectRatio: AnimatedHeaderRatio = .ratio16_9
39
- public var isSurface: Bool = true
40
- public var composable: (_ scrollState: CGFloat) -> AnyView = { _ in AnyView(EmptyView()) }
41
-
42
- public init(
43
- aspectRatio: AnimatedHeaderRatio = .ratio16_9,
44
- isSurface: Bool = true,
45
- composable: @escaping (_ scrollState: CGFloat) -> AnyView = { _ in AnyView(EmptyView()) }
46
- ) {
47
- self.aspectRatio = aspectRatio
48
- self.isSurface = isSurface
49
- self.composable = composable
50
- }
51
- }
52
-
53
- // MARK: - Utils
54
-
55
- func safeAreaTopInset() -> CGFloat {
56
- if #available(iOS 15.0, *) {
57
- return UIApplication.shared.connectedScenes
58
- .compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.top }
59
- .first ?? 0
60
- } else {
61
- return UIApplication.shared.connectedScenes
62
- .compactMap { ($0 as? UIWindowScene)?.windows.first?.safeAreaInsets.top }
63
- .first ?? 0
64
- }
65
- }
66
-
67
- // MARK: - Header Background
68
-
69
- public struct HeaderBackground: View {
70
- var headerType: HeaderType = .default
71
- var scrollState: CGFloat
72
- var headerTransparent: Bool = false
73
- var fullScreenContent: Bool = false
74
-
75
- public init(
76
- headerType: HeaderType = .default,
77
- scrollState: CGFloat = 0,
78
- headerTransparent: Bool = false,
79
- fullScreenContent: Bool = false
80
- ) {
81
- self.headerType = headerType
82
- self.scrollState = scrollState
83
- self.headerTransparent = headerTransparent
84
- self.fullScreenContent = fullScreenContent
85
- }
86
-
87
- public var body: some View {
88
- let statusBarHeight = safeAreaTopInset()
89
- let opacity = max(0, 1 - scrollState / 200)
90
- let height = fullScreenContent ? 0 : statusBarHeight + 52
91
- let backgroundColor = headerTransparent ? Color.clear : Colors.black01
92
-
93
- Group {
94
- switch headerType {
95
- case .default:
96
- ZStack(alignment: .bottom) {
97
- Rectangle()
98
- .fill(backgroundColor)
99
- .opacity(headerTransparent ? 0 : opacity)
100
- .frame(height: height)
101
- .shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
102
- .background(backgroundColor)
103
- .overlay(
104
- headerTransparent ? AnyView(EmptyView()) :
105
- AnyView(
106
- Rectangle()
107
- .fill(LinearGradient(
108
- gradient: Gradient(colors: [
109
- Color(red: 1, green: 0.8, blue: 0.87),
110
- Color(red: 1, green: 0.8, blue: 0.87).opacity(0.5)
111
- ]),
112
- startPoint: .top,
113
- endPoint: .bottom))
114
- .opacity(opacity)
115
- .frame(height: height))
116
- )
117
- if !headerTransparent {
118
- Rectangle()
119
- .fill(Color.black.opacity(0.1))
120
- .frame(height: 1)
121
- .frame(maxWidth: .infinity)
122
- }
123
- }
124
-
125
- case .extended:
126
- ZStack {
127
- if !headerTransparent {
128
- Colors.black01
129
- LinearGradient(
130
- gradient: Gradient(colors: [
131
- Color(red: 1, green: 0.8, blue: 0.87),
132
- Color(red: 1, green: 0.8, blue: 0.87).opacity(0)
133
- ]),
134
- startPoint: .top,
135
- endPoint: .bottom
136
- )
137
- .opacity(opacity)
138
- .frame(height: 154)
139
- }
140
- }
141
-
142
- case .none:
143
- EmptyView()
144
- }
145
- }
146
- }
147
- }
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
-
165
- // MARK: - Header View
166
-
167
- public struct Header: View {
168
- var headerType: HeaderType = .default
169
- var titlePosition: TitlePosition
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?
174
- var headerRight: (()->any View)? = {HeaderRight()}
175
- var goBack: (() -> Void)?
176
- var opacity: CGFloat = 1
177
- var animatedHeader: AnimatedHeader?
178
- var scrollState: CGFloat = 0
179
- var inputSearchProps: InputSearchProps?
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
183
-
184
- public init(
185
- headerType: HeaderType = .default,
186
- titlePosition: TitlePosition,
187
- title: String,
188
- headerTitle: HeaderTitle? = nil,
189
- headerRight: (()->any View)? = {HeaderRight()},
190
- goBack: (() -> Void)? = nil,
191
- opacity: CGFloat = 1,
192
- animatedHeader: AnimatedHeader? = nil,
193
- scrollState: CGFloat = 0,
194
- inputSearchProps: InputSearchProps? = nil,
195
- tintColor: Color? = nil,
196
- titleOpacity: CGFloat = 1
197
- ) {
198
- self.headerType = headerType
199
- self.titlePosition = titlePosition
200
- self.title = title
201
- self.headerTitle = headerTitle
202
- self.headerRight = headerRight
203
- self.goBack = goBack
204
- self.opacity = opacity
205
- self.animatedHeader = animatedHeader
206
- self.scrollState = scrollState
207
- self.inputSearchProps = inputSearchProps
208
- self.tintColor = tintColor
209
- self.titleOpacity = titleOpacity
210
- }
211
-
212
- public var body: some View {
213
-
214
- let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
215
- let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
216
-
217
- if headerType != .none {
218
- VStack(spacing: 0) {
219
- ZStack {
220
- // MARK: - Left and Right Controls
221
- HStack {
222
- if let goBack = goBack {
223
- SwiftUI.Button(action: goBack) {
224
- Circle()
225
- .stroke(borderColor, lineWidth: 0.2)
226
- .background(Circle().fill(backgroundButtonColor))
227
- .frame(width: 28, height: 28)
228
- .overlay(
229
- Icon(source: "arrow-back", size: 20, color: tintColor ?? Colors.black17)
230
- )
231
- }
232
- }
233
-
234
- Spacer()
235
-
236
- if let headerRight = headerRight {
237
- AnyView(headerRight())
238
- }
239
- }
240
- .padding(.horizontal, 12)
241
-
242
- // MARK: - Title or Search
243
- HStack {
244
- if titlePosition == .left && goBack != nil {
245
- Spacer().frame(width: 38)
246
- }
247
-
248
- if let inputProps = inputSearchProps {
249
- InputSearch(props: inputProps)
250
- } else if case let .user(userData)? = headerTitle {
251
- if #available(iOS 16.0, *) {
252
- HeaderUserView(userData)
253
- }
254
- } else {
255
- MomoText(headerTitle?.defaultTitle ?? title, typography: .headerDefaultBold, color: tintColor ?? Colors.black17, maxLines: 1)
256
- .opacity(titleOpacity)
257
- .frame(maxWidth: titlePosition == .center ? UIScreen.main.bounds.width - 252 : nil)
258
- .frame(maxWidth: titlePosition == .center ? .infinity : UIScreen.main.bounds.width - 172, alignment: titlePosition == .center ? .center : .leading)
259
- }
260
-
261
- Spacer()
262
- }
263
- .padding(.horizontal)
264
- }
265
- .background(Color.clear)
266
- .frame(height: 52)
267
- }
268
- }
269
- }
270
- }
@@ -1,22 +0,0 @@
1
- import Foundation
2
-
3
- public protocol ComposeApi {
4
- func request(funcName: String, params: Any, _ completion: ((String) -> Void)?)
5
- func request(funcName: String, params: Any) -> String
6
- }
7
-
8
- // Default implementation
9
- public class ComposeApiImpl: ComposeApi {
10
- public static let shared = ComposeApiImpl()
11
-
12
- private init() {}
13
-
14
- public func request(funcName: String, params: Any, _ completion: ((String) -> Void)?) {
15
- // Implement your API request logic here
16
- }
17
-
18
- public func request(funcName: String, params: Any) -> String {
19
- // Implement your API request logic here
20
- return ""
21
- }
22
- }
@@ -1,306 +0,0 @@
1
- import SwiftUI
2
- import Foundation
3
-
4
- public struct HeaderRightData {
5
- var useShortcut: Bool
6
- var useMore: Bool
7
- var tools: [ToolGroup]
8
- var toolCallback: ((String) -> Void)?
9
- var useSystemTools: Bool
10
-
11
- public init(
12
- useShortcut: Bool = false,
13
- useMore: Bool = false,
14
- tools: [ToolGroup] = [],
15
- toolCallback: ((String) -> Void)? = nil,
16
- useSystemTools: Bool = true
17
- ) {
18
- self.useShortcut = useShortcut
19
- self.useMore = useMore
20
- self.tools = tools
21
- self.toolCallback = toolCallback
22
- self.useSystemTools = useSystemTools
23
- }
24
- }
25
-
26
- public struct Tool {
27
- var key: String
28
- var icon: String
29
- var showBadge: Bool
30
- var name: [String: String]
31
- var showRightIcon: Bool
32
-
33
- public init(
34
- key: String,
35
- icon: String,
36
- name: [String: String] = [:],
37
- showBadge: Bool = false,
38
- showRightIcon: Bool = true
39
- ) {
40
- self.key = key
41
- self.icon = icon
42
- self.showBadge = showBadge
43
- self.name = name
44
- self.showRightIcon = showRightIcon
45
- }
46
-
47
- func toMap() -> [String: Any] {
48
- return [
49
- "key": key,
50
- "icon": icon,
51
- "showBadge": showBadge,
52
- "name": name,
53
- "showRightIcon": showRightIcon
54
- ]
55
- }
56
- }
57
-
58
- public struct ToolGroup {
59
- var title: [String: String]
60
- var items: [Tool]
61
-
62
- public init(
63
- title: [String: String] = [:],
64
- items: [Tool]
65
- ) {
66
- self.title = title
67
- self.items = items
68
- }
69
-
70
- func toMap() -> [String: Any] {
71
- return [
72
- "title": title,
73
- "items": items.map { $0.toMap() }
74
- ]
75
- }
76
- }
77
-
78
- struct NavigationButtonConfig {
79
- let icon: String
80
- let onPress: () -> Void
81
- }
82
-
83
- // MARK: - Components
84
- public struct HeaderRight: View {
85
- public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
86
- self.headerRight = headerRight
87
- self.tintColor = tintColor
88
- }
89
- var headerRight: HeaderRightData?
90
- var tintColor: Color? = nil
91
-
92
- public var body: some View {
93
- HStack(alignment: .center) {
94
- ToolkitHeaderRight(headerRight: headerRight, tintColor: tintColor)
95
- }
96
- }
97
- }
98
-
99
- struct ToolkitHeaderRight: View {
100
- var headerRight: HeaderRightData?
101
- var tintColor: Color?
102
- @State private var isFavorite: Bool = false
103
- @State private var isLoading: Bool = false
104
- @EnvironmentObject private var environment: ApplicationEnvironment
105
-
106
- // MARK: - Actions
107
- private func onPressShortcut() {
108
- // onToolAction needs the app context (appId/code) so the host knows which app to (un)favorite;
109
- // without it the favorite is a no-op. Mirrors onPressMore below and the Compose HeaderRight.
110
- let context = environment.applicationContext
111
- environment.composeApi?.request(
112
- funcName: "onToolAction",
113
- params: [
114
- "item": ["key": "onFavorite"],
115
- "context": [
116
- "appId": context?.appId,
117
- "code": context?.appCode,
118
- "name": context?.appName,
119
- "icon": context?.appIcon,
120
- "description": context?.description,
121
- "support": context?.support,
122
- "toolkitConfig": context?.toolkitConfig,
123
- "providerId": context?.providerId,
124
- "permissions": context?.permissions
125
- ]
126
- ]
127
- ) { _ in
128
- isFavorite.toggle()
129
- }
130
- }
131
-
132
- private func onPressHelpCenter() {
133
- let context = environment.applicationContext
134
- let paramMap: [String: Any] = [
135
- "appId": context?.appId,
136
- "code": context?.appCode,
137
- "name": context?.appName,
138
- "icon": context?.appIcon,
139
- "description": context?.description
140
- ]
141
- environment.composeApi?.request(
142
- funcName: "showHelpCenter",
143
- params: paramMap
144
- ) { _ in }
145
- }
146
-
147
- private func onPressClose() {
148
- environment.composeApi?.request(
149
- funcName: "dismissAll",
150
- params: ""
151
- ) { _ in }
152
- }
153
-
154
- private func onPressMore() {
155
- let context = environment.applicationContext
156
- let params: [String: Any] = [
157
- "useSystemTools": headerRight?.useSystemTools,
158
- "tools": headerRight?.tools.map { $0.toMap() } ?? [],
159
- "context": [
160
- "appId": context?.appId,
161
- "code": context?.appCode,
162
- "name": context?.appName,
163
- "icon": context?.appIcon,
164
- "description": context?.description,
165
- "support": context?.support,
166
- "toolkitConfig": context?.toolkitConfig,
167
- "providerId": context?.providerId,
168
- "permissions": context?.permissions
169
- ]
170
- ]
171
- environment.composeApi?.request(
172
- funcName: "showTools",
173
- params: params
174
- ) { response in
175
- do {
176
- if let jsonData = response.data(using: .utf8),
177
- let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
178
- let toolResponse = json["response"] as? String {
179
- headerRight?.toolCallback?(toolResponse)
180
- }
181
- } catch {
182
- print("Error parsing response:", error)
183
- }
184
- }
185
- }
186
-
187
- // Load the current favorite state on appear so the star reflects reality; without this the button
188
- // always renders "not favorited" until the user taps it. Mirrors the Compose LaunchedEffect.
189
- private func loadFavoriteState() {
190
- let context = environment.applicationContext
191
- environment.composeApi?.request(
192
- funcName: "isFavoriteApp",
193
- params: ["code": context?.appCode]
194
- ) { response in
195
- guard let data = response.data(using: .utf8),
196
- let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
197
- return
198
- }
199
- isFavorite = (json["response"] as? Bool) ?? false
200
- }
201
- }
202
-
203
- private func getNavigationButtonConfig() -> NavigationButtonConfig {
204
- let totalTools = headerRight?.tools.reduce(0) { $0 + $1.items.count } ?? 0
205
- var icon = isFavorite ? "pin_star_checked" : "pin_star"
206
- var onClickHandler: () -> Void = onPressShortcut
207
-
208
- if totalTools > 1 || headerRight?.useMore == true {
209
- icon = "navigation_more_icon"
210
- onClickHandler = onPressMore
211
- } else if totalTools == 1, let singleTool = headerRight?.tools.first?.items.first {
212
- icon = singleTool.icon
213
- onClickHandler = {
214
- headerRight?.toolCallback?(singleTool.key)
215
- }
216
- }
217
-
218
- return NavigationButtonConfig(icon: icon, onPress: onClickHandler)
219
- }
220
-
221
- var body: some View {
222
- let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
223
- let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
224
- let navButtonConfig = getNavigationButtonConfig()
225
- let showBadge = headerRight?.tools.contains { group in
226
- group.items.contains { $0.showBadge }
227
- } ?? false
228
-
229
- HStack(alignment: .center, spacing: 0) {
230
- if headerRight?.useShortcut == true {
231
- NavigationButton(
232
- disabled: isLoading,
233
- icon: navButtonConfig.icon,
234
- showBadge: showBadge,
235
- onClick: navButtonConfig.onPress,
236
- tintColor: tintColor
237
- )
238
- }
239
-
240
- HStack(alignment: .center, spacing: 0) {
241
- Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
242
- .padding(4)
243
- .onTapGesture {
244
- onPressHelpCenter()
245
- }
246
-
247
- Rectangle()
248
- .fill(tintColor ?? Colors.black20)
249
- .frame(width: 0.5, height: 12)
250
-
251
- Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
252
- .padding(4)
253
- .onTapGesture {
254
- onPressClose()
255
- }
256
- }
257
- .frame(width: 65, height: 28)
258
- .background(backgroundButtonColor)
259
- .cornerRadius(14)
260
- .overlay(
261
- RoundedRectangle(cornerRadius: 14)
262
- .stroke(borderColor, lineWidth: 0.2)
263
- )
264
- .padding(.leading, Spacing.S)
265
- }
266
- .onAppear {
267
- loadFavoriteState()
268
- }
269
- }
270
- }
271
-
272
- struct NavigationButton: View {
273
- var disabled: Bool
274
- var icon: String
275
- var showBadge: Bool
276
- var onClick: () -> Void
277
- var tintColor: Color?
278
-
279
- var body: some View {
280
- let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
281
- let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
282
-
283
- ZStack {
284
- Circle()
285
- .fill(backgroundButtonColor)
286
- .overlay(
287
- Circle()
288
- .stroke(borderColor, lineWidth: 0.2)
289
- )
290
-
291
- Icon(source: icon, size: 16, color: tintColor)
292
-
293
- if showBadge {
294
- BadgeDot(size: .small)
295
- .offset(x: -2, y: -2)
296
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
297
- }
298
- }
299
- .frame(width: 28, height: 28)
300
- .onTapGesture {
301
- if !disabled {
302
- onClick()
303
- }
304
- }
305
- }
306
- }
@@ -1,104 +0,0 @@
1
- //
2
- // BottomTab.swift
3
- // MoMoUIKits
4
- //
5
- // SwiftUI mirror of Compose's `BottomTab` (navigation/bottomtab/BottomTab.kt).
6
- // Hosts a set of tabbed screens, each wrapped in `StackScreen` chrome built
7
- // from that tab's `NavigationOptions`, with a `BottomTabBar` pinned to the
8
- // bottom. Switching tabs cross-fades + scales the content (parity with Compose).
9
- //
10
-
11
- import SwiftUI
12
-
13
- @available(iOS 16.0, *)
14
- public struct BottomTab: View {
15
- private let items: [BottomTabItem]
16
- private let floatingButton: BottomTabFloatingButton?
17
-
18
- @EnvironmentObject private var navigator: Navigator
19
- @State private var selectedIndex: Int = 0
20
-
21
- public init(items: [BottomTabItem], floatingButton: BottomTabFloatingButton? = nil) {
22
- self.items = items
23
- self.floatingButton = floatingButton
24
- }
25
-
26
- private var bottomInset: CGFloat { safeAreaBottomInset() }
27
- private var reservedBottom: CGFloat { BOTTOM_TAB_BAR_HEIGHT + bottomInset + Spacing.S }
28
-
29
- public var body: some View {
30
- ZStack(alignment: .bottom) {
31
- // Tab content area, inset above the bar.
32
- ZStack {
33
- ForEach(items.indices, id: \.self) { index in
34
- if index == selectedIndex {
35
- tabScreen(for: index)
36
- .transition(.scale(scale: 0.97).combined(with: .opacity))
37
- }
38
- }
39
- }
40
- .padding(.bottom, reservedBottom)
41
- .animation(.easeInOut(duration: 0.2), value: selectedIndex)
42
-
43
- // Tab bar + home-indicator filler.
44
- VStack(spacing: 0) {
45
- BottomTabBar(
46
- items: items,
47
- floatingButton: floatingButton,
48
- selectedIndex: selectedIndex,
49
- onTabSelected: { index in
50
- if index != selectedIndex { selectedIndex = index }
51
- }
52
- )
53
- Colors.black01
54
- .frame(height: bottomInset + Spacing.S)
55
- }
56
- }
57
- .frame(maxWidth: .infinity, maxHeight: .infinity)
58
- .ignoresSafeArea(edges: .bottom)
59
- .preference(key: IsBottomTabRootKey.self, value: true)
60
- }
61
-
62
- @ViewBuilder
63
- private func tabScreen(for index: Int) -> some View {
64
- let item = items[index]
65
- let options = item.options ?? NavigationOptions()
66
- StackScreen(
67
- backgroundColor: options.backgroundColor,
68
- headerTransparent: options.headerTransparent,
69
- fullScreenContent: options.fullScreenContent,
70
- tintColor: options.tintColor ?? Colors.black17,
71
- isBack: !options.hiddenBack,
72
- headerType: options.headerType,
73
- useAnimated: options.useAnimated,
74
- title: options.title,
75
- headerTitle: options.headerTitle,
76
- titlePosition: options.titlePosition,
77
- // Tab back: non-first tab returns to the first tab, first tab
78
- // delegates to the navigator (mirrors Compose's onBackHandler).
79
- goBack: options.hiddenBack ? nil : {
80
- if index != 0 { selectedIndex = 0 } else { navigator.onBackSafe() }
81
- },
82
- scrollable: options.scrollable,
83
- scrollToTopCallback: options.scrollToTopCallback,
84
- useAvoidKeyboard: options.useAvoidKeyboard,
85
- keyboardShouldPersistTaps: options.keyboardShouldPersistTaps,
86
- footer: options.footer,
87
- headerRight: options.resolvedHeaderRight,
88
- animatedHeader: options.animatedHeader,
89
- layoutOffset: options.layoutOffset,
90
- inputSearchProps: options.inputSearchProps,
91
- fabProps: options.fabProps,
92
- content: { item.screen() }
93
- )
94
- }
95
- }
96
-
97
- /// Bottom safe-area inset (home indicator).
98
- @available(iOS 16.0, *)
99
- private func safeAreaBottomInset() -> CGFloat {
100
- let bottomInset = UIApplication.shared.connectedScenes
101
- .compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.bottom }
102
- .first ?? 0
103
- return min(bottomInset, 21)
104
- }