@momo-kits/native-kits 0.0.1-beta.4 → 0.89.1-2

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 (195) hide show
  1. package/README.md +1 -0
  2. package/android/.gradle/8.0/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.0/dependencies-accessors/dependencies-accessors.lock +0 -0
  4. package/android/.gradle/8.0/dependencies-accessors/gc.properties +0 -0
  5. package/android/.gradle/8.0/fileChanges/last-build.bin +0 -0
  6. package/android/.gradle/8.0/fileHashes/fileHashes.lock +0 -0
  7. package/android/.gradle/8.0/gc.properties +0 -0
  8. package/android/.gradle/config.properties +2 -0
  9. package/android/.gradle/vcs-1/gc.properties +0 -0
  10. package/android/.idea/gradle.xml +12 -0
  11. package/android/.idea/migrations.xml +10 -0
  12. package/android/.idea/misc.xml +10 -0
  13. package/android/.idea/vcs.xml +6 -0
  14. package/android/.idea/workspace.xml +50 -0
  15. package/android/build.gradle +35 -69
  16. package/android/local.properties +8 -0
  17. package/android/proguard-rules.pro +21 -0
  18. package/android/src/main/AndroidManifest.xml +2 -2
  19. package/android/src/main/java/com/momoplatform/nativekits/foundations/Button.kt +16 -0
  20. package/android/src/main/java/com/momoplatform/nativekits/libs/Logo.kt +63 -0
  21. package/android/src/main/java/com/momoplatform/nativekits/libs/TrustBanner.kt +246 -0
  22. package/android/src/main/res/drawable/ic_launcher_background.xml +170 -0
  23. package/android/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
  24. package/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  25. package/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  26. package/android/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  27. package/android/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  28. package/android/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  29. package/android/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  30. package/android/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  31. package/android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  32. package/android/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  33. package/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  34. package/android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  35. package/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  36. package/android/src/main/res/values/colors.xml +10 -0
  37. package/android/src/main/res/values/strings.xml +3 -0
  38. package/android/src/main/res/values/themes.xml +3 -0
  39. package/android/src/main/res/values-night/themes.xml +3 -0
  40. package/android/src/main/res/xml/backup_rules.xml +13 -0
  41. package/android/src/main/res/xml/data_extraction_rules.xml +19 -0
  42. package/build.gradle.kts +18 -0
  43. package/compose/build.gradle.kts +82 -0
  44. package/compose/src/androidMain/AndroidManifest.xml +2 -0
  45. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Platform.kt +3 -0
  46. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +8 -0
  47. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Utils.android.kt +14 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Components.kt +194 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +197 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +133 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +62 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +288 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +80 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +42 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +126 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +51 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +299 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +246 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +223 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +244 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +281 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PromotionPopup.kt +72 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +65 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +64 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +63 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +73 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +42 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +150 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +173 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +242 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +2 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +50 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1321 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Platform.kt +3 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +5 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +97 -0
  83. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Platform.kt +3 -0
  84. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +9 -0
  85. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Utils.ios.kt +18 -0
  86. package/gradle.properties +19 -0
  87. package/gradlew +240 -0
  88. package/gradlew.bat +91 -0
  89. package/ios/Button/Button.swift +9 -2
  90. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
  91. package/ios/Checkbox/Checkbox.swift +38 -0
  92. package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Colors.swift +21 -19
  93. package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
  94. package/ios/Image/Image.swift +60 -0
  95. package/ios/Input/Input.swift +88 -37
  96. package/ios/OTPKeyboard/KeyboardButton.swift +12 -4
  97. package/ios/OTPKeyboard/OTPKeyboard.swift +1 -1
  98. package/ios/Popup/PopupDisplay.swift +169 -0
  99. package/ios/Popup/PopupInput.swift +96 -0
  100. package/ios/Popup/PopupPromotion.swift +52 -0
  101. package/ios/PopupView/FullscreenPopup.swift +251 -0
  102. package/ios/{Popup → PopupView}/Modifiers.swift +6 -20
  103. package/ios/{Popup → PopupView}/PopupView.swift +38 -146
  104. package/ios/PopupView/Utils++.swift +281 -0
  105. package/ios/ScrollIndicator/ScrollIndicator.swift +79 -0
  106. package/ios/Switch/Switch.swift +44 -0
  107. package/ios/Template/Logo/Logo.swift +75 -0
  108. package/ios/Template/TrustBanner/TrustBanner.swift +189 -0
  109. package/ios/native-kits.podspec +2 -0
  110. package/local.properties +8 -0
  111. package/package.json +6 -6
  112. package/publish.sh +1 -10
  113. package/settings.gradle.kts +41 -0
  114. package/android/gradle.properties +0 -5
  115. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsPackage.kt +0 -17
  116. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsViewManager.kt +0 -20
  117. package/example/ios/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -63
  118. package/example/ios/Example/Assets.xcassets/Background.colorset/Contents.json +0 -38
  119. package/example/ios/Example/Assets.xcassets/Border.colorset/Contents.json +0 -38
  120. package/example/ios/Example/Assets.xcassets/Card.colorset/Contents.json +0 -38
  121. package/example/ios/Example/Assets.xcassets/Contents.json +0 -6
  122. package/example/ios/Example/Assets.xcassets/Error.colorset/Contents.json +0 -38
  123. package/example/ios/Example/Assets.xcassets/Primary.colorset/Contents.json +0 -38
  124. package/example/ios/Example/Assets.xcassets/PrimaryDark.colorset/Contents.json +0 -38
  125. package/example/ios/Example/Assets.xcassets/PrimaryLight.colorset/Contents.json +0 -38
  126. package/example/ios/Example/Assets.xcassets/Secondary.colorset/Contents.json +0 -38
  127. package/example/ios/Example/Assets.xcassets/Success.colorset/Contents.json +0 -38
  128. package/example/ios/Example/Assets.xcassets/Text.colorset/Contents.json +0 -38
  129. package/example/ios/Example/Assets.xcassets/TextSecondary.colorset/Contents.json +0 -38
  130. package/example/ios/Example/Assets.xcassets/Warning.colorset/Contents.json +0 -38
  131. package/example/ios/Example/ComponentDetail.swift +0 -35
  132. package/example/ios/Example/ComponentItem.swift +0 -24
  133. package/example/ios/Example/ComponentModel.swift +0 -33
  134. package/example/ios/Example/ContentView.swift +0 -53
  135. package/example/ios/Example/Example.entitlements +0 -10
  136. package/example/ios/Example/Foundation/ButtonDemo.swift +0 -80
  137. package/example/ios/Example/Foundation/Popup/ActionSheets.swift +0 -136
  138. package/example/ios/Example/Foundation/Popup/Floats.swift +0 -127
  139. package/example/ios/Example/Foundation/Popup/Popups.swift +0 -101
  140. package/example/ios/Example/Foundation/PopupDemo.swift +0 -366
  141. package/example/ios/Example/Foundation/TextInputDemo.swift +0 -31
  142. package/example/ios/Example/Foundation/TypographyDemo.swift +0 -53
  143. package/example/ios/Example/Foundation.swift +0 -26
  144. package/example/ios/Example/Info.plist +0 -18
  145. package/example/ios/Example/Library/SwipeableDemo.swift +0 -105
  146. package/example/ios/Example/Library.swift +0 -26
  147. package/example/ios/Example/Main.swift +0 -11
  148. package/example/ios/Example/Overview.swift +0 -20
  149. package/example/ios/Example/Preview Content/Preview Assets.xcassets/Contents.json +0 -6
  150. package/example/ios/Example/Resource/SFProText-Black.ttf +0 -0
  151. package/example/ios/Example/Resource/SFProText-Bold.ttf +0 -0
  152. package/example/ios/Example/Resource/SFProText-Heavy.ttf +0 -0
  153. package/example/ios/Example/Resource/SFProText-Light.ttf +0 -0
  154. package/example/ios/Example/Resource/SFProText-Medium.ttf +0 -0
  155. package/example/ios/Example/Resource/SFProText-Regular.ttf +0 -0
  156. package/example/ios/Example/Resource/SFProText-Semibold.ttf +0 -0
  157. package/example/ios/Example/Resource/SFProText-Thin.ttf +0 -0
  158. package/example/ios/Example/Resource/SFProText-Ultralight.ttf +0 -0
  159. package/example/ios/Example/Resource/foundation.json +0 -33
  160. package/example/ios/Example/Resource/library.json +0 -8
  161. package/example/ios/Example/Utils.swift +0 -30
  162. package/example/ios/Example/ViewModifier/DarkViewModifier.swift +0 -22
  163. package/example/ios/Example.xcodeproj/project.pbxproj +0 -570
  164. package/example/ios/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  165. package/example/ios/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  166. package/example/ios/Example.xcodeproj/project.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  167. package/example/ios/Example.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  168. package/example/ios/Example.xcworkspace/contents.xcworkspacedata +0 -10
  169. package/example/ios/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  170. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  171. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  172. package/example/ios/Podfile +0 -6
  173. package/example/ios/Podfile.lock +0 -16
  174. package/example/ios/Pods/Local Podspecs/MoMoUIKits.podspec.json +0 -29
  175. package/example/ios/Pods/Manifest.lock +0 -16
  176. package/example/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -661
  177. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  178. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  179. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -25
  180. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-dummy.m +0 -5
  181. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-prefix.pch +0 -12
  182. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-umbrella.h +0 -16
  183. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.debug.xcconfig +0 -12
  184. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.modulemap +0 -6
  185. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.release.xcconfig +0 -12
  186. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown +0 -3
  187. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist +0 -29
  188. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m +0 -5
  189. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h +0 -16
  190. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig +0 -14
  191. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap +0 -6
  192. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig +0 -14
  193. package/ios/Colors+Spacing/Radius.swift +0 -11
  194. package/ios/Popup/Utils++.swift +0 -163
  195. /package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Spacing.swift +0 -0
@@ -0,0 +1,281 @@
1
+ import SwiftUI
2
+ import Combine
3
+
4
+ final class DispatchWorkHolder {
5
+ var work: DispatchWorkItem?
6
+ }
7
+
8
+ final class ClassReference<T> {
9
+ var value: T
10
+
11
+ init(_ value: T) {
12
+ self.value = value
13
+ }
14
+ }
15
+
16
+ public extension View {
17
+
18
+ @ViewBuilder
19
+ func valueChanged<T: Equatable>(value: T, onChange: @escaping (T) -> Void) -> some View {
20
+ if #available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *) {
21
+ self.onChange(of: value, perform: onChange)
22
+ } else {
23
+ self.onReceive(Just(value)) { value in
24
+ onChange(value)
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ public extension View {
31
+ @ViewBuilder
32
+ func applyIf<T: View>(_ condition: Bool, apply: (Self) -> T) -> some View {
33
+ if condition {
34
+ apply(self)
35
+ } else {
36
+ self
37
+ }
38
+ }
39
+
40
+ @ViewBuilder
41
+ func addTapIf(if condition: Bool, onTap: @escaping ()->()) -> some View {
42
+ if condition {
43
+ self.simultaneousGesture(
44
+ TapGesture().onEnded {
45
+ onTap()
46
+ }
47
+ )
48
+ } else {
49
+ self
50
+ }
51
+ }
52
+ }
53
+
54
+ struct FrameGetter: ViewModifier {
55
+
56
+ @Binding var frame: CGRect
57
+
58
+ func body(content: Content) -> some View {
59
+ content
60
+ .background(
61
+ GeometryReader { proxy -> AnyView in
62
+ DispatchQueue.main.async {
63
+ let rect = proxy.frame(in: .global)
64
+ // This avoids an infinite layout loop
65
+ if rect.integral != self.frame.integral {
66
+ self.frame = rect
67
+ }
68
+ }
69
+ return AnyView(EmptyView())
70
+ }
71
+ )
72
+ }
73
+ }
74
+
75
+ extension View {
76
+ public func frameGetter(_ frame: Binding<CGRect>) -> some View {
77
+ modifier(FrameGetter(frame: frame))
78
+ }
79
+ }
80
+
81
+ struct AnimationCompletionObserverModifier<Value>: AnimatableModifier where Value: VectorArithmetic, Value: Comparable {
82
+
83
+ /// While animating, SwiftUI changes the old input value to the new target value using this property. This value is set to the old value until the animation completes.
84
+ var animatableData: Value {
85
+ didSet {
86
+ notifyCompletionIfFinished()
87
+ }
88
+ }
89
+
90
+ /// The target value for which we're observing. This value is directly set once the animation starts. During animation, `animatableData` will hold the oldValue and is only updated to the target value once the animation completes.
91
+ private var targetValue: Value
92
+
93
+ /// The completion callback which is called once the animation completes.
94
+ private var completion: () -> Void
95
+
96
+ init(observedValue: Value, completion: @escaping () -> Void) {
97
+ self.completion = completion
98
+ self.animatableData = observedValue
99
+ targetValue = observedValue
100
+ }
101
+
102
+ /// Verifies whether the current animation is finished and calls the completion callback if true.
103
+ private func notifyCompletionIfFinished() {
104
+ guard animatableData == targetValue else { return }
105
+
106
+ /// Dispatching is needed to take the next runloop for the completion callback.
107
+ /// This prevents errors like "Modifying state during view update, this will cause undefined behavior."
108
+ DispatchQueue.main.async {
109
+ self.completion()
110
+ }
111
+ }
112
+
113
+ func body(content: Content) -> some View {
114
+ /// We're not really modifying the view so we can directly return the original input value.
115
+ return content
116
+ }
117
+ }
118
+
119
+ struct AnimatableModifierDouble: AnimatableModifier {
120
+
121
+ var targetValue: Double
122
+ static var done = false
123
+
124
+ // SwiftUI gradually varies it from old value to the new value
125
+ var animatableData: Double {
126
+ didSet {
127
+ checkIfFinished()
128
+ }
129
+ }
130
+ var completion: () -> ()
131
+
132
+ // Re-created every time the control argument changes
133
+ init(bindedValue: Double, completion: @escaping () -> ()) {
134
+ self.completion = completion
135
+
136
+ // Set animatableData to the new value. But SwiftUI again directly
137
+ // and gradually varies the value while the body
138
+ // is being called to animate. Following line serves the purpose of
139
+ // associating the extenal argument with the animatableData.
140
+ self.animatableData = bindedValue
141
+ targetValue = bindedValue
142
+ AnimatableModifierDouble.done = false
143
+ }
144
+
145
+ func checkIfFinished() -> () {
146
+ if AnimatableModifierDouble.done { return }
147
+ let delta = 0.1
148
+ if animatableData > targetValue - delta &&
149
+ animatableData < targetValue + delta {
150
+ //print("check", animatableData, targetValue)
151
+ AnimatableModifierDouble.done = true
152
+ DispatchQueue.main.async {
153
+ self.completion()
154
+ }
155
+ }
156
+ }
157
+
158
+ func body(content: Content) -> some View {
159
+ content
160
+ }
161
+ }
162
+
163
+ extension View {
164
+
165
+ func onAnimationCompleted(for value: Double, completion: @escaping () -> Void) -> some View {
166
+ modifier(AnimatableModifierDouble(bindedValue: value, completion: completion))
167
+ }
168
+ }
169
+
170
+ extension UIApplication {
171
+ var keyWindow: UIWindow? {
172
+ connectedScenes
173
+ .compactMap {
174
+ $0 as? UIWindowScene
175
+ }
176
+ .flatMap {
177
+ $0.windows
178
+ }
179
+ .first {
180
+ $0.isKeyWindow
181
+ }
182
+ }
183
+ }
184
+
185
+ private struct SafeAreaInsetsKey: EnvironmentKey {
186
+ static var defaultValue: EdgeInsets {
187
+ UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets()
188
+ }
189
+ }
190
+
191
+ extension EnvironmentValues {
192
+ var safeAreaInsets: EdgeInsets {
193
+ self[SafeAreaInsetsKey.self]
194
+ }
195
+ }
196
+
197
+ private extension UIEdgeInsets {
198
+ var swiftUiInsets: EdgeInsets {
199
+ EdgeInsets(top: top, leading: left, bottom: bottom, trailing: right)
200
+ }
201
+ }
202
+
203
+ extension View {
204
+
205
+ func transparentNonAnimatingFullScreenCover<Content: View>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View {
206
+ modifier(TransparentNonAnimatableFullScreenModifier(isPresented: isPresented, fullScreenContent: content))
207
+ }
208
+ }
209
+
210
+ private struct TransparentNonAnimatableFullScreenModifier<FullScreenContent: View>: ViewModifier {
211
+
212
+ @Binding var isPresented: Bool
213
+ let fullScreenContent: () -> (FullScreenContent)
214
+
215
+ func body(content: Content) -> some View {
216
+ if #available(iOS 14.0, *) {
217
+ content
218
+ .valueChanged(value: isPresented, onChange: { isPresented in
219
+ UIView.setAnimationsEnabled(false)
220
+ })
221
+ .fullScreenCover(isPresented: $isPresented, content: {
222
+ ZStack {
223
+ fullScreenContent()
224
+ }
225
+ .background(FullScreenCoverBackgroundRemovalView())
226
+ .onAppear {
227
+ if !UIView.areAnimationsEnabled {
228
+ UIView.setAnimationsEnabled(true)
229
+ }
230
+ }
231
+ .onDisappear {
232
+ if !UIView.areAnimationsEnabled {
233
+ UIView.setAnimationsEnabled(true)
234
+ }
235
+ }
236
+ })
237
+ } else {
238
+ content
239
+ .valueChanged(value: isPresented, onChange: { isPresented in
240
+ UIView.setAnimationsEnabled(false)
241
+ })
242
+ .sheet(isPresented: $isPresented, content: {
243
+ ZStack {
244
+ fullScreenContent()
245
+ }
246
+ .background(FullScreenCoverBackgroundRemovalView())
247
+ .onAppear {
248
+ if !UIView.areAnimationsEnabled {
249
+ UIView.setAnimationsEnabled(true)
250
+ }
251
+ }
252
+ .onDisappear {
253
+ if !UIView.areAnimationsEnabled {
254
+ UIView.setAnimationsEnabled(true)
255
+ }
256
+ }
257
+ })
258
+ }
259
+ }
260
+
261
+ }
262
+
263
+ private struct FullScreenCoverBackgroundRemovalView: UIViewRepresentable {
264
+
265
+ private class BackgroundRemovalView: UIView {
266
+
267
+ override func didMoveToWindow() {
268
+ super.didMoveToWindow()
269
+
270
+ superview?.superview?.backgroundColor = .clear
271
+ }
272
+
273
+ }
274
+
275
+ func makeUIView(context: Context) -> UIView {
276
+ return BackgroundRemovalView()
277
+ }
278
+
279
+ func updateUIView(_ uiView: UIView, context: Context) {}
280
+
281
+ }
@@ -0,0 +1,79 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ public struct ScrollViewWithScrollBar<Content: View>: View {
5
+ let INDICATOR_HEIGHT: CGFloat = 4
6
+ @State var position = CGPoint(x: 0, y: 2) //2 is INDICATOR_HEIGHT/2
7
+ @State var contentSize: CGFloat = 0
8
+ @State var scrollViewWidth: CGFloat = 0
9
+ var indicatorWidth: CGFloat
10
+ var indicatorContainerWidth: CGFloat
11
+ var indicatorColor: Color
12
+ var indicatorContainerColor: Color
13
+ @ViewBuilder var content: () -> Content
14
+
15
+ public init(indicatorWidth: CGFloat = 12, indicatorContainerWidth: CGFloat = 36,indicatorColor: Color = Colors.primary, indicatorContainerColor: Color = Colors.black06,@ViewBuilder content: @escaping () -> Content) {
16
+ self.indicatorWidth = indicatorWidth
17
+ self.indicatorContainerWidth = indicatorContainerWidth
18
+ self.content = content
19
+ self.indicatorColor = indicatorColor
20
+ self.indicatorContainerColor = indicatorContainerColor
21
+ }
22
+
23
+ // MARK: Public
24
+
25
+ public var body: some View {
26
+ return VStack {
27
+ ScrollView(.horizontal, showsIndicators: false) {
28
+ HStack {
29
+ content()
30
+ }.background(GeometryReader {
31
+ proxy in
32
+ Color.clear.onAppear {
33
+ self.contentSize = proxy.size.width
34
+ self.position = CGPoint(x: indicatorWidth / 2, y: INDICATOR_HEIGHT / 2)
35
+ }
36
+ Color.clear.preference(key: ViewOffsetKey.self,
37
+ value: -proxy.frame(in: .named("scroll")).origin.x)
38
+
39
+ })
40
+ .onPreferenceChange(ViewOffsetKey.self) {
41
+ let newPosition = $0 * ((indicatorContainerWidth - indicatorWidth) / (contentSize - scrollViewWidth)) + indicatorWidth / 2
42
+ if (newPosition > indicatorWidth / 2 && newPosition < indicatorContainerWidth - indicatorWidth / 2 + 1)
43
+ {
44
+ self.position = CGPoint(x: newPosition, y: INDICATOR_HEIGHT / 2)
45
+ }
46
+ }
47
+ }.background(GeometryReader {
48
+ proxy in
49
+ Color.clear.onAppear {
50
+ self.scrollViewWidth = proxy.size.width
51
+ }
52
+ }).coordinateSpace(name: "scroll")
53
+
54
+ HStack {
55
+ HStack {}.frame(width: indicatorWidth, height: INDICATOR_HEIGHT)
56
+ .background(indicatorColor)
57
+ .cornerRadius(Radius.S)
58
+ .position(position)
59
+ }.frame(width: indicatorContainerWidth, height: INDICATOR_HEIGHT, alignment: .leading)
60
+ .background(indicatorContainerColor).cornerRadius(Radius.S)
61
+ }
62
+
63
+
64
+
65
+ }
66
+
67
+ }
68
+
69
+ // MARK: - ViewOffsetKey
70
+
71
+ struct ViewOffsetKey: PreferenceKey {
72
+ typealias Value = CGFloat
73
+
74
+ static var defaultValue = CGFloat.zero
75
+
76
+ static func reduce(value: inout Value, nextValue: () -> Value) {
77
+ value += nextValue()
78
+ }
79
+ }
@@ -0,0 +1,44 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ public struct Switch: View{
5
+ @Binding var value: Bool
6
+ var disabled: Bool
7
+ var onChange: ((Bool)->Void)?
8
+
9
+ public init(_ value: Binding<Bool>,disabled: Bool = false, onChange: ((Bool)->Void)? = nil){
10
+ self._value = value
11
+ self.disabled = disabled
12
+ self.onChange = onChange
13
+ }
14
+
15
+
16
+ public var body: some View {
17
+ SwiftUI.Button(action: {
18
+ withAnimation{
19
+ value.toggle()
20
+ onChange?(value)
21
+ }
22
+ }){
23
+ HStack{
24
+ HStack {
25
+ }
26
+ .frame(width: 6, height: 6)
27
+ .background(value ? Colors.pink03 : Colors.black07)
28
+ .clipShape(Circle())
29
+ .opacity(disabled ? 0.4 : 1)
30
+ }
31
+ .frame(width: 14, height: 14)
32
+ .background(Colors.black01)
33
+ .clipShape(Circle())
34
+ .padding(.leading,5)
35
+ .padding(.trailing,5)
36
+
37
+ }
38
+ .disabled(disabled)
39
+ .frame(width: 40, height: 24, alignment: value == true ? .trailing : .leading)
40
+ .background(value ? Colors.pink03 : Colors.black07)
41
+ .clipShape(Capsule())
42
+ .opacity(disabled ? 0.4 : 1)
43
+ }
44
+ }
@@ -0,0 +1,75 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ public struct LogoView: View {
5
+ var type: String
6
+ var useShadow: Bool
7
+ var useBorder: Bool
8
+ var useBackground : Bool
9
+ var backgroundColor: Color
10
+ var shadowColor : Color
11
+ var borderColor : Color
12
+ var source: String
13
+ var radius: CGFloat
14
+
15
+ public init(source: String = "",
16
+ type: String = "image",
17
+ useBackground : Bool = true,
18
+ useShadow: Bool = true,
19
+ useBorder : Bool = true,
20
+ backgroundColor: Color = Colors.black01,
21
+ shadowColor : Color = Color(hex: "d9d9d9"),
22
+ borderColor : Color = Color(hex: "e2e2e2")){
23
+ self.type = type
24
+ self.useShadow = useShadow
25
+ self.useBorder = useBorder
26
+ self.useBackground = useBackground
27
+ self.backgroundColor = backgroundColor
28
+ self.borderColor = borderColor
29
+ self.source = source
30
+ self.shadowColor = shadowColor
31
+ self.radius = type == "image" ? 7 : 0
32
+ }
33
+
34
+
35
+ public var body: some View {
36
+ return ImageView(source)
37
+ .scaledToFit()
38
+ .frame(width: 28, height: 28)
39
+ .cornerRadius(radius)
40
+ .containerBorder(type, useBorder, borderColor)
41
+ .containerBackground(type, useBackground, backgroundColor)
42
+ .containerShadow(useShadow, shadowColor)
43
+ }
44
+
45
+ }
46
+
47
+ private extension View {
48
+ func containerBorder (_ type: String, _ useBorder: Bool, _ borderColor: Color) -> some View {
49
+ Group {
50
+ if (useBorder && type != "icon") {
51
+ self.overlay(RoundedRectangle(cornerRadius: 8).stroke(borderColor, lineWidth: 1))
52
+ } else {
53
+ self
54
+ }
55
+ }
56
+ }
57
+ func containerBackground (_ type: String, _ useBackground: Bool, _ backgroundColor: Color) -> some View {
58
+ Group {
59
+ if (useBackground && type != "icon") {
60
+ self.background(backgroundColor)
61
+ } else {
62
+ self
63
+ }
64
+ }
65
+ }
66
+ func containerShadow (_ useShadow: Bool, _ shadowColor: Color) -> some View {
67
+ Group {
68
+ if useShadow {
69
+ self.shadow(color: shadowColor, radius: 2, x: 0, y: 2)
70
+ } else {
71
+ self
72
+ }
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,189 @@
1
+ //
2
+ // TrustBanner.swift
3
+ // MoMoUIKits
4
+ //
5
+ // Created by Nguyễn Hoàng Sơn on 14/12/2023.
6
+ //
7
+
8
+ import Foundation
9
+ import SwiftUI
10
+ import SDWebImageSwiftUI
11
+
12
+ let jsonUrl = "https://static.momocdn.net/app/json/component-kits/design_system.json"
13
+
14
+ struct TrustBannerData: Decodable {
15
+ let trustBanner: TrustBanner
16
+ }
17
+
18
+ struct TrustBanner: Decodable {
19
+ let content: Dictionary<String, String>
20
+ let pciImage: String
21
+ let sslImage: String
22
+ let urlConfig: String
23
+ let featureCode: String
24
+ let icons: [String]
25
+ let momoImage: String
26
+
27
+ enum CodingKeys: String, CodingKey {
28
+ case content, pciImage, sslImage, icons, momoImage
29
+ case urlConfig = "url_config"
30
+ case featureCode = "feature_code"
31
+ }
32
+ }
33
+
34
+ let defaultBanner = TrustBanner(
35
+ content: ["vi": "Bảo mật thông tin & An toàn tài sản của bạn là ưu tiên hàng đầu của MoMo.",
36
+ "en": "Your data security and money safety are MoMo's top priorities."],
37
+ pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci.png",
38
+ sslImage: "https://static.momocdn.net/app/img/kits/trustBanner/ssl.png",
39
+ urlConfig: "https://momo.vn/an-toan-bao-mat",
40
+ featureCode: "login_and_security",
41
+ icons: [
42
+ "https://static.momocdn.net/app/img/kits/trustBanner/ic_viettinbank.png",
43
+ "https://static.momocdn.net/app/img/kits/trustBanner/ic_agribank.png",
44
+ "https://static.momocdn.net/app/img/kits/trustBanner/ic_vietcombank.png",
45
+ "https://static.momocdn.net/app/img/kits/trustBanner/ic_bidv.png"
46
+ ],
47
+ momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_momo_secu.png"
48
+ )
49
+
50
+ let defaultData = TrustBannerData(trustBanner: defaultBanner)
51
+
52
+ class TrustBannerViewModel: ObservableObject {
53
+ @Published var trustBanner: TrustBannerData?
54
+
55
+ init() {
56
+ fetchData(from: jsonUrl)
57
+ }
58
+
59
+ func fetchData(from urlString: String) {
60
+ guard let url = URL(string: urlString) else { return }
61
+
62
+ URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
63
+ guard let self = self else { return }
64
+
65
+ if let error = error {
66
+ print("Data fetching error: \(error)")
67
+ return
68
+ }
69
+
70
+ guard let data = data else {
71
+ print("No data received")
72
+ return
73
+ }
74
+ do {
75
+ let decodedData = try JSONDecoder().decode(TrustBannerData.self, from: data)
76
+ DispatchQueue.main.async {
77
+ self.trustBanner = decodedData
78
+ }
79
+ } catch {
80
+ print("Decoding error: \(error)")
81
+ }
82
+ }.resume()
83
+ }
84
+ }
85
+
86
+ public struct TrustBannerView: View {
87
+ var serviceName: String? = ""
88
+ var screenName: String? = ""
89
+ var onPress: ((_ params: NSDictionary) -> Void)?
90
+ var trackEvent: ((_ eventName: String, _ params: NSDictionary) -> Void)?
91
+ var language: String?
92
+
93
+ @ObservedObject private var viewModel = TrustBannerViewModel()
94
+
95
+ public init(serviceName: String?,
96
+ screenName: String?,
97
+ onPress: ((_ params: NSDictionary) -> Void)?,
98
+ trackEvent: ((_ eventName: String,_ params: NSDictionary) -> Void)?, language: String? = "vi") {
99
+ if let trackEvent = trackEvent {
100
+ trackEvent("service_component_displayed", ["service_name": serviceName ?? "", "screen_name": screenName ?? "", "component_name": "logo_trust"])
101
+ }
102
+ self.onPress = onPress
103
+ self.trackEvent = trackEvent
104
+ self.screenName = screenName
105
+ self.serviceName = serviceName
106
+ self.language = language
107
+ }
108
+
109
+ public var body: some View {
110
+ if let trustBanner = viewModel.trustBanner {
111
+ TrustBannerContentView(trustBanner: trustBanner.trustBanner, onPress: onPress, trackEvent: trackEvent, serviceName: serviceName, screenName: screenName, language: language)
112
+ }
113
+ }
114
+ }
115
+
116
+ struct TrustBannerContentView: View {
117
+ var trustBanner: TrustBanner
118
+ var onPress: ((_ params: NSDictionary) -> Void)?
119
+ var trackEvent: ((_ eventName: String, _ params: NSDictionary) -> Void)?
120
+ var serviceName: String?
121
+ var screenName: String?
122
+ var language: String?
123
+
124
+ var body: some View {
125
+ GeometryReader { geo in
126
+ HStack(alignment: .center, spacing: 8) {
127
+ WebImage(url: URL(string: trustBanner.momoImage))
128
+ .resizable()
129
+ .scaledToFit()
130
+ .frame(width: 64, height: 64)
131
+ VStack(alignment: .leading, spacing: 8){
132
+ Text(trustBanner.content[language ?? "vi"] ?? "")
133
+ .foregroundColor(Color(hex: 0xFF484848))
134
+ .lineLimit(2)
135
+ .font(.system(size: 12, weight: Font.Weight.regular))
136
+ .frame(width: geo.size.width - 24 - 64 - 8, height: 40,alignment: .leading)
137
+ HStack{
138
+ HStack{
139
+ WebImage(url: URL(string: trustBanner.pciImage))
140
+ .resizable()
141
+ .scaledToFit()
142
+ .frame(width: 52, height: 20)
143
+ WebImage(url: URL(string: trustBanner.sslImage))
144
+ .resizable()
145
+ .scaledToFit()
146
+ .frame(width: 52, height: 20)
147
+ }
148
+ Spacer()
149
+ HStack(spacing: -6){
150
+ ForEach(trustBanner.icons, id: \.self) { iconURL in
151
+ WebImage(url: URL(string: iconURL))
152
+ .resizable()
153
+ .scaledToFit()
154
+ .frame(width:24, height: 24)
155
+ .background(Color.white)
156
+ .cornerRadius(12)
157
+ .overlay(
158
+ Circle().stroke(Color(hex: 0xFFE8E8E8), lineWidth: 1)
159
+ )
160
+ }
161
+ Text("36+")
162
+ .font(.system(size: 10, weight: Font.Weight.regular))
163
+ .frame(width: 24, height: 24, alignment: .center)
164
+ .foregroundColor(Color(hex: 0xFFEB2F96))
165
+ .background(Color(hex: 0xFFFDEAF4))
166
+ .cornerRadius(12)
167
+ .overlay(
168
+ Circle()
169
+ .stroke(Color(hex: 0xFFE8E8E8), lineWidth: 1)
170
+ )
171
+ }.frame(width: 96, height: 24)
172
+ }
173
+ }
174
+ }
175
+ .frame(width: geo.size.width - 24)
176
+ .padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
177
+ .background(Color(hex: 0xFFF2F8FF))
178
+ .cornerRadius(12)
179
+ .onTapGesture {
180
+ if let trackEvent = trackEvent {
181
+ trackEvent("service_component_clicked", ["service_name": self.serviceName ?? "", "screen_name": self.screenName ?? "", "component_name": "logo_trust", "url_config": trustBanner.urlConfig])
182
+ }
183
+ if let onPress = onPress {
184
+ onPress(["service_name": self.serviceName ?? "", "screen_name": self.screenName ?? "", "component_name": "logo_trust", "url_config": trustBanner.urlConfig, "feature_code": trustBanner.featureCode])
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
@@ -12,4 +12,6 @@ Pod::Spec.new do |spec|
12
12
  spec.source_files = "**/*.swift"
13
13
 
14
14
  spec.framework = 'SwiftUI', 'Combine'
15
+ spec.dependency 'SDWebImageSwiftUI'
16
+ spec.dependency 'SkeletonUI', '1.0.11'
15
17
  end
@@ -0,0 +1,8 @@
1
+ ## This file must *NOT* be checked into Version Control Systems,
2
+ # as it contains information specific to your local configuration.
3
+ #
4
+ # Location of the SDK. This is only used by Gradle.
5
+ # For customization when using a Version Control System, please read the
6
+ # header note.
7
+ #Mon Mar 04 18:29:01 ICT 2024
8
+ sdk.dir=/Users/wem/Library/Android/sdk