@momo-kits/native-kits 0.0.1-beta.4 → 0.2.0-kt22
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.
- package/README.md +176 -5
- package/ios/Application/ApplicationEnvironment.swift +50 -0
- package/ios/Application/Components.swift +263 -0
- package/ios/Application/ComposeApi.swift +22 -0
- package/ios/Application/FloatingButton.swift +172 -0
- package/ios/Application/HeaderRight.swift +271 -0
- package/ios/Application/Screen.swift +249 -0
- package/ios/Badge/Badge.swift +85 -0
- package/ios/Badge/BadgeDot.swift +31 -0
- package/ios/Badge/BadgeRibbon.swift +242 -0
- package/ios/Button/Button.swift +169 -89
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
- package/ios/Checkbox/Checkbox.swift +81 -0
- package/ios/Chip/Chip.swift +96 -0
- package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Colors.swift +21 -19
- package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
- package/ios/Icon/Icon.swift +51 -0
- package/ios/Image/Image.swift +70 -0
- package/ios/Input/ErrorView.swift +55 -0
- package/ios/Input/Input.swift +159 -50
- package/ios/Input/InputPhoneNumber.swift +158 -0
- package/ios/Input/InputSearch.swift +238 -0
- package/ios/Input/InputTextArea.swift +242 -0
- package/ios/Lottie/LottieView.swift +86 -0
- package/ios/OTPKeyboard/KeyboardButton.swift +12 -4
- package/ios/OTPKeyboard/OTPKeyboard.swift +1 -1
- package/ios/Popup/PopupDisplay.swift +287 -0
- package/ios/Popup/PopupInput.swift +96 -0
- package/ios/Popup/PopupPromotion.swift +73 -0
- package/ios/PopupView/FullscreenPopup.swift +251 -0
- package/ios/{Popup → PopupView}/Modifiers.swift +6 -20
- package/ios/{Popup → PopupView}/PopupView.swift +38 -146
- package/ios/PopupView/Utils++.swift +281 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
- package/ios/Swipeable/SwipeCell.swift +39 -31
- package/ios/Switch/Switch.swift +44 -0
- package/ios/Template/Logo/Logo.swift +75 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
- package/ios/Typography/Text.swift +140 -0
- package/ios/Typography/Typography.swift +70 -5
- package/ios/native-kits.podspec +20 -15
- package/package.json +6 -6
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -114
- package/LICENSE +0 -20
- package/android/build.gradle +0 -82
- package/android/gradle.properties +0 -5
- package/android/src/main/AndroidManifest.xml +0 -4
- package/android/src/main/java/com/momoplatform/nativekits/NativeKitsPackage.kt +0 -17
- package/android/src/main/java/com/momoplatform/nativekits/NativeKitsViewManager.kt +0 -20
- package/example/ios/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -63
- package/example/ios/Example/Assets.xcassets/Background.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Border.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Card.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Contents.json +0 -6
- package/example/ios/Example/Assets.xcassets/Error.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Primary.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/PrimaryDark.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/PrimaryLight.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Secondary.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Success.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Text.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/TextSecondary.colorset/Contents.json +0 -38
- package/example/ios/Example/Assets.xcassets/Warning.colorset/Contents.json +0 -38
- package/example/ios/Example/ComponentDetail.swift +0 -35
- package/example/ios/Example/ComponentItem.swift +0 -24
- package/example/ios/Example/ComponentModel.swift +0 -33
- package/example/ios/Example/ContentView.swift +0 -53
- package/example/ios/Example/Example.entitlements +0 -10
- package/example/ios/Example/Foundation/ButtonDemo.swift +0 -80
- package/example/ios/Example/Foundation/Popup/ActionSheets.swift +0 -136
- package/example/ios/Example/Foundation/Popup/Floats.swift +0 -127
- package/example/ios/Example/Foundation/Popup/Popups.swift +0 -101
- package/example/ios/Example/Foundation/PopupDemo.swift +0 -366
- package/example/ios/Example/Foundation/TextInputDemo.swift +0 -31
- package/example/ios/Example/Foundation/TypographyDemo.swift +0 -53
- package/example/ios/Example/Foundation.swift +0 -26
- package/example/ios/Example/Info.plist +0 -18
- package/example/ios/Example/Library/SwipeableDemo.swift +0 -105
- package/example/ios/Example/Library.swift +0 -26
- package/example/ios/Example/Main.swift +0 -11
- package/example/ios/Example/Overview.swift +0 -20
- package/example/ios/Example/Preview Content/Preview Assets.xcassets/Contents.json +0 -6
- package/example/ios/Example/Resource/SFProText-Black.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Bold.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Heavy.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Light.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Medium.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Regular.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Semibold.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Thin.ttf +0 -0
- package/example/ios/Example/Resource/SFProText-Ultralight.ttf +0 -0
- package/example/ios/Example/Resource/foundation.json +0 -33
- package/example/ios/Example/Resource/library.json +0 -8
- package/example/ios/Example/Utils.swift +0 -30
- package/example/ios/Example/ViewModifier/DarkViewModifier.swift +0 -22
- package/example/ios/Example.xcodeproj/project.pbxproj +0 -570
- package/example/ios/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/example/ios/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/example/ios/Example.xcodeproj/project.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/example/ios/Example.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/ios/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/example/ios/Podfile +0 -6
- package/example/ios/Podfile.lock +0 -16
- package/example/ios/Pods/Local Podspecs/MoMoUIKits.podspec.json +0 -29
- package/example/ios/Pods/Manifest.lock +0 -16
- package/example/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -661
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
- package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -25
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-dummy.m +0 -5
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-prefix.pch +0 -12
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-umbrella.h +0 -16
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.debug.xcconfig +0 -12
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.modulemap +0 -6
- package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.release.xcconfig +0 -12
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown +0 -3
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist +0 -29
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m +0 -5
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h +0 -16
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig +0 -14
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap +0 -6
- package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig +0 -14
- package/ios/Colors+Spacing/Radius.swift +0 -11
- package/ios/Popup/Utils++.swift +0 -163
- package/ios/Theme.md +0 -18
- package/publish.sh +0 -28
- /package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Spacing.swift +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//
|
|
2
|
+
// LottieView.swift
|
|
3
|
+
// MoMoPlatform
|
|
4
|
+
//
|
|
5
|
+
// Created by thanhdat on 16/01/2023.
|
|
6
|
+
// Copyright © 2023 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import SwiftUI
|
|
10
|
+
import Lottie
|
|
11
|
+
|
|
12
|
+
struct LottieView: UIViewRepresentable {
|
|
13
|
+
var name: String = ""
|
|
14
|
+
var url: String?
|
|
15
|
+
var loopMode: LottieLoopMode = .playOnce
|
|
16
|
+
var contentMode : UIView.ContentMode = .scaleAspectFit
|
|
17
|
+
var onDone: (() -> Void)?
|
|
18
|
+
|
|
19
|
+
// Add static cache for animations
|
|
20
|
+
private static var animationCache = NSCache<NSString, LottieAnimation>()
|
|
21
|
+
|
|
22
|
+
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
|
|
23
|
+
let view = UIView(frame: .zero)
|
|
24
|
+
|
|
25
|
+
// Configure Lottie with optimized settings
|
|
26
|
+
let configuration = LottieConfiguration(
|
|
27
|
+
renderingEngine: .coreAnimation,
|
|
28
|
+
decodingStrategy: .dictionaryBased
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
let animationView = LottieAnimationView(configuration: configuration)
|
|
32
|
+
|
|
33
|
+
if let url = url {
|
|
34
|
+
// Use cached animation if available
|
|
35
|
+
if let cachedAnimation = Self.animationCache.object(forKey: url as NSString) {
|
|
36
|
+
animationView.animation = cachedAnimation
|
|
37
|
+
setupAnimationView(animationView, view)
|
|
38
|
+
} else {
|
|
39
|
+
LottieAnimation.loadedFrom(url: URL(string: url)!, closure: { animation in
|
|
40
|
+
if let animation = animation {
|
|
41
|
+
Self.animationCache.setObject(animation, forKey: url as NSString)
|
|
42
|
+
animationView.animation = animation
|
|
43
|
+
setupAnimationView(animationView, view)
|
|
44
|
+
}
|
|
45
|
+
}, animationCache: nil)
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
// Use cached animation if available
|
|
49
|
+
if let cachedAnimation = Self.animationCache.object(forKey: name as NSString) {
|
|
50
|
+
animationView.animation = cachedAnimation
|
|
51
|
+
setupAnimationView(animationView, view)
|
|
52
|
+
} else if let animation = LottieAnimation.named(name) {
|
|
53
|
+
Self.animationCache.setObject(animation, forKey: name as NSString)
|
|
54
|
+
animationView.animation = animation
|
|
55
|
+
setupAnimationView(animationView, view)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return view
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private func setupAnimationView(_ animationView: LottieAnimationView, _ containerView: UIView) {
|
|
63
|
+
animationView.contentMode = contentMode
|
|
64
|
+
animationView.loopMode = loopMode
|
|
65
|
+
animationView.backgroundBehavior = loopMode == .loop ? .pauseAndRestore : .pause
|
|
66
|
+
|
|
67
|
+
// Optimize rendering
|
|
68
|
+
animationView.shouldRasterizeWhenIdle = true
|
|
69
|
+
|
|
70
|
+
animationView.translatesAutoresizingMaskIntoConstraints = false
|
|
71
|
+
containerView.addSubview(animationView)
|
|
72
|
+
|
|
73
|
+
NSLayoutConstraint.activate([
|
|
74
|
+
animationView.widthAnchor.constraint(equalTo: containerView.widthAnchor),
|
|
75
|
+
animationView.heightAnchor.constraint(equalTo: containerView.heightAnchor)
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
animationView.play { finished in
|
|
79
|
+
if finished {
|
|
80
|
+
onDone?()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {}
|
|
86
|
+
}
|
|
@@ -8,9 +8,13 @@ typealias SwiftButton = SwiftUI.Button
|
|
|
8
8
|
public struct KeyboardButton: View {
|
|
9
9
|
// MARK: Lifecycle
|
|
10
10
|
|
|
11
|
-
public init(_ key: String, action: @escaping (_ key: String)->Void) {
|
|
11
|
+
public init(_ key: String, action: @escaping (_ key: String)->Void, width: CGFloat = .infinity, height: CGFloat = 48, color: Color = Colors.card, textColor: Color = Colors.text) {
|
|
12
12
|
self.key = key
|
|
13
13
|
self.action = action
|
|
14
|
+
self.width = width
|
|
15
|
+
self.height = height
|
|
16
|
+
self.color = color
|
|
17
|
+
self.textColor = textColor
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
// MARK: Public
|
|
@@ -19,9 +23,9 @@ public struct KeyboardButton: View {
|
|
|
19
23
|
SwiftUI.Button(action: { self.action(key) }) {
|
|
20
24
|
HStack {
|
|
21
25
|
Text(key).font(.system(size: 24, weight: .bold))
|
|
22
|
-
}.frame(maxWidth:
|
|
23
|
-
.background(
|
|
24
|
-
.foregroundColor(
|
|
26
|
+
}.frame(maxWidth: CGFloat(width), minHeight: height)
|
|
27
|
+
.background(color)
|
|
28
|
+
.foregroundColor(textColor)
|
|
25
29
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
26
30
|
}
|
|
27
31
|
}
|
|
@@ -30,4 +34,8 @@ public struct KeyboardButton: View {
|
|
|
30
34
|
|
|
31
35
|
var key: String = ""
|
|
32
36
|
var action: (_ key: String)->Void
|
|
37
|
+
var width: CGFloat
|
|
38
|
+
var height: CGFloat
|
|
39
|
+
var color: Color
|
|
40
|
+
var textColor: Color
|
|
33
41
|
}
|
|
@@ -119,7 +119,7 @@ public struct OTPKeyboard: View {
|
|
|
119
119
|
private struct ValueView: View {
|
|
120
120
|
// MARK: Lifecycle
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
init(_ value: String, valueLength: Int) {
|
|
123
123
|
self.value = value
|
|
124
124
|
self.valueLength = valueLength
|
|
125
125
|
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import SDWebImageSwiftUI
|
|
2
|
+
import SwiftUI
|
|
3
|
+
|
|
4
|
+
// MARK: - PopupButtonDirection
|
|
5
|
+
|
|
6
|
+
public enum PopupButtonDirection {
|
|
7
|
+
case row
|
|
8
|
+
case column
|
|
9
|
+
case auto
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// MARK: - PopupButtonType
|
|
13
|
+
|
|
14
|
+
public enum PopupButtonType {
|
|
15
|
+
case text
|
|
16
|
+
case button
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
extension View {
|
|
20
|
+
func measureLineHeights(font: Font,
|
|
21
|
+
oneLine: Binding<CGFloat>,
|
|
22
|
+
twoLines: Binding<CGFloat>) -> some View {
|
|
23
|
+
overlay(
|
|
24
|
+
VStack(spacing: 0) {
|
|
25
|
+
Text("A")
|
|
26
|
+
.font(font)
|
|
27
|
+
.lineLimit(1)
|
|
28
|
+
.fixedSize()
|
|
29
|
+
.background(
|
|
30
|
+
GeometryReader { g in
|
|
31
|
+
Color.clear.onAppear { oneLine.wrappedValue = g.size.height }
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
.hidden()
|
|
35
|
+
|
|
36
|
+
Text("A\nA")
|
|
37
|
+
.font(font)
|
|
38
|
+
.lineLimit(2)
|
|
39
|
+
.fixedSize(horizontal: false, vertical: true)
|
|
40
|
+
.background(
|
|
41
|
+
GeometryReader { g in
|
|
42
|
+
Color.clear.onAppear { twoLines.wrappedValue = g.size.height }
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
.hidden()
|
|
46
|
+
}
|
|
47
|
+
.frame(width: 0, height: 0)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MARK: - PopupDisplay
|
|
53
|
+
|
|
54
|
+
public struct PopupDisplay: View {
|
|
55
|
+
// MARK: Lifecycle
|
|
56
|
+
|
|
57
|
+
public init(isPresented: Binding<Bool>, title: String = "", description: String = "", url: String = "", buttonDirection: PopupButtonDirection = .column, buttonType: PopupButtonType = .button, actionButtonTitle: String = "", closeButtonTitle: String = "", onPressAction: @escaping () -> Void = {}, onPressCloseButton: @escaping () -> Void = {}, onClose: @escaping () -> Void = {}, errorCode: String = "", isShowCloseIcon: Bool = true) {
|
|
58
|
+
self._isPresented = isPresented
|
|
59
|
+
self.title = title
|
|
60
|
+
self.description = description
|
|
61
|
+
self.url = url
|
|
62
|
+
self.buttonDirection = buttonDirection
|
|
63
|
+
self.buttonType = buttonType
|
|
64
|
+
self.onPressAction = onPressAction
|
|
65
|
+
self.onPressCloseButton = onPressCloseButton
|
|
66
|
+
self.onClose = onClose
|
|
67
|
+
self.closeButtonTitle = closeButtonTitle
|
|
68
|
+
self.actionButtonTitle = actionButtonTitle
|
|
69
|
+
self.errorCode = errorCode
|
|
70
|
+
self.isShowCloseIcon = isShowCloseIcon
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// MARK: Public
|
|
74
|
+
|
|
75
|
+
@State private var oneLineH: CGFloat = 0
|
|
76
|
+
@State private var twoLineH: CGFloat = 0
|
|
77
|
+
|
|
78
|
+
private var fallbackLH: CGFloat { UIFont.preferredFont(forTextStyle: .body).lineHeight }
|
|
79
|
+
private var perLine: CGFloat { max(twoLineH - oneLineH, oneLineH > 0 ? oneLineH : fallbackLH) }
|
|
80
|
+
|
|
81
|
+
private func heightForLines(lines: CGFloat) -> CGFloat {
|
|
82
|
+
guard oneLineH > 0 && twoLineH > 0 else { return ceil(fallbackLH * lines) }
|
|
83
|
+
return ceil(oneLineH + max(0, lines - 1) * (twoLineH - oneLineH))
|
|
84
|
+
}
|
|
85
|
+
private var maxHeight8_5: CGFloat { heightForLines(lines: 8.5) }
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@State private var textHeight: CGFloat = .zero
|
|
89
|
+
@State private var isScrollable = false
|
|
90
|
+
|
|
91
|
+
public var body: some View {
|
|
92
|
+
let language = UserDefaults.standard.string(forKey: "language")?.replacingOccurrences(of: "\"", with: "") ?? "vi"
|
|
93
|
+
let errorCodeLabels: [String: String] = ["vi": "Mã lỗi: ", "en": "Error code: "]
|
|
94
|
+
|
|
95
|
+
var shouldUseColumn: Bool {
|
|
96
|
+
buttonDirection == .column || (buttonDirection == .auto && (closeButtonTitle.count > 12 || actionButtonTitle.count > 12))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
VStack(spacing: 12) {
|
|
100
|
+
ZStack(alignment: .topTrailing) {
|
|
101
|
+
if(isShowCloseIcon) {
|
|
102
|
+
SwiftUI.Button(action: onClose) {
|
|
103
|
+
Image(systemName: "xmark.circle.fill")
|
|
104
|
+
.resizable()
|
|
105
|
+
.frame(width: 22, height: 22)
|
|
106
|
+
.overlay(RoundedRectangle(cornerRadius: Radius.L).stroke(Colors.black01, lineWidth: 2))
|
|
107
|
+
}.foregroundColor(Colors.black20)
|
|
108
|
+
.background(Colors.black01.cornerRadius(15))
|
|
109
|
+
.padding(.trailing, -11)
|
|
110
|
+
.padding(.top, -11)
|
|
111
|
+
.zIndex(1)
|
|
112
|
+
.accessibility(identifier: "ic_popup_close")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
VStack(spacing: 0) {
|
|
116
|
+
if(!url.isEmpty) {
|
|
117
|
+
WebImage(url: URL(string: url), isAnimating: .constant(true))
|
|
118
|
+
.resizable()
|
|
119
|
+
.placeholder {
|
|
120
|
+
Rectangle()
|
|
121
|
+
.fill(Color.gray.opacity(0.3))
|
|
122
|
+
.frame(maxWidth: .infinity, maxHeight: 184)
|
|
123
|
+
}
|
|
124
|
+
.aspectRatio(1.777, contentMode: .fit)
|
|
125
|
+
.frame(maxWidth: .infinity, alignment: .center)
|
|
126
|
+
.clipShape(RoundedCorner(radius: 15, corners: [.topLeft, .topRight]))
|
|
127
|
+
.clipped()
|
|
128
|
+
}
|
|
129
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
130
|
+
Text(title)
|
|
131
|
+
.foregroundColor(.black)
|
|
132
|
+
.font(.header_default_semibold)
|
|
133
|
+
.padding(.top, 24)
|
|
134
|
+
.padding(.bottom, 8)
|
|
135
|
+
.lineLimit(2)
|
|
136
|
+
.accessibility(identifier: "title_popup_permission")
|
|
137
|
+
|
|
138
|
+
Group {
|
|
139
|
+
if isScrollable {
|
|
140
|
+
ScrollView(showsIndicators: false) {
|
|
141
|
+
Text(description)
|
|
142
|
+
.font(.body_default_regular)
|
|
143
|
+
.foregroundColor(Colors.black17)
|
|
144
|
+
.multilineTextAlignment(.leading)
|
|
145
|
+
.background(GeometryReader { geo in
|
|
146
|
+
Color.clear.onAppear { textHeight = geo.size.height }
|
|
147
|
+
})
|
|
148
|
+
.measureLineHeights(font: .body_default_regular,
|
|
149
|
+
oneLine: $oneLineH,
|
|
150
|
+
twoLines: $twoLineH)
|
|
151
|
+
}
|
|
152
|
+
// Cap the visible height to ~8.5 lines
|
|
153
|
+
.frame(height: min(maxHeight8_5, textHeight))
|
|
154
|
+
} else {
|
|
155
|
+
Text(description)
|
|
156
|
+
.font(.body_default_regular)
|
|
157
|
+
.foregroundColor(Colors.black17)
|
|
158
|
+
.multilineTextAlignment(.leading)
|
|
159
|
+
.background(GeometryReader { geo in
|
|
160
|
+
Color.clear.onAppear {
|
|
161
|
+
textHeight = geo.size.height
|
|
162
|
+
// Trigger scroll at ~8.5 lines
|
|
163
|
+
isScrollable = textHeight > maxHeight8_5
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
.measureLineHeights(font: .body_default_regular,
|
|
167
|
+
oneLine: $oneLineH,
|
|
168
|
+
twoLines: $twoLineH)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
.padding(.bottom, 8)
|
|
172
|
+
|
|
173
|
+
if(!errorCode.isEmpty) {
|
|
174
|
+
Text((errorCodeLabels[language] ?? "Mã lỗi: ") + errorCode)
|
|
175
|
+
.foregroundColor(Colors.black12)
|
|
176
|
+
.font(.description_xs_regular)
|
|
177
|
+
.lineLimit(1)
|
|
178
|
+
.padding(.bottom, 8)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
182
|
+
.padding(.horizontal, 24)
|
|
183
|
+
.padding(.bottom, 16)
|
|
184
|
+
|
|
185
|
+
if shouldUseColumn {
|
|
186
|
+
if buttonType == .text {
|
|
187
|
+
ColumnTextButtons
|
|
188
|
+
} else {
|
|
189
|
+
ColumnButtons
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
if buttonType == .text {
|
|
193
|
+
RowTextButtons
|
|
194
|
+
} else {
|
|
195
|
+
RowButtons
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
.fixedSize(horizontal: false, vertical: true)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
.background(Color.white.cornerRadius(15))
|
|
203
|
+
.padding(.horizontal, 12)
|
|
204
|
+
.accessibilityElement(children: .ignore)
|
|
205
|
+
.accessibility(identifier: "popup_notify")
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// MARK: Internal
|
|
209
|
+
|
|
210
|
+
@Binding var isPresented: Bool
|
|
211
|
+
var title: String
|
|
212
|
+
var description: String
|
|
213
|
+
var url: String
|
|
214
|
+
var buttonDirection: PopupButtonDirection
|
|
215
|
+
var buttonType: PopupButtonType
|
|
216
|
+
var onPressAction: () -> Void
|
|
217
|
+
var onPressCloseButton: () -> Void
|
|
218
|
+
var onClose: () -> Void
|
|
219
|
+
var actionButtonTitle: String
|
|
220
|
+
var closeButtonTitle: String
|
|
221
|
+
var errorCode: String
|
|
222
|
+
var isShowCloseIcon: Bool
|
|
223
|
+
|
|
224
|
+
var RowTextButtons: some View {
|
|
225
|
+
HStack {
|
|
226
|
+
SwiftUI.Button(action: onPressCloseButton) {
|
|
227
|
+
Text(closeButtonTitle)
|
|
228
|
+
.font(.action2)
|
|
229
|
+
}.foregroundColor(Colors.black09)
|
|
230
|
+
.padding(.trailing, 12)
|
|
231
|
+
|
|
232
|
+
SwiftUI.Button(action: onPressAction) {
|
|
233
|
+
Text(actionButtonTitle)
|
|
234
|
+
.font(.action2)
|
|
235
|
+
}.foregroundColor(Colors.primary)
|
|
236
|
+
}
|
|
237
|
+
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
238
|
+
.padding(EdgeInsets(top: 0, leading: 0, bottom: 24, trailing: 24))
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
var ColumnTextButtons: some View {
|
|
242
|
+
VStack(alignment: .trailing) {
|
|
243
|
+
SwiftUI.Button(action: onPressAction) {
|
|
244
|
+
Text(actionButtonTitle)
|
|
245
|
+
.font(.action2)
|
|
246
|
+
}.foregroundColor(Colors.primary)
|
|
247
|
+
.padding(.bottom, 6)
|
|
248
|
+
|
|
249
|
+
SwiftUI.Button(action: onPressCloseButton) {
|
|
250
|
+
Text(closeButtonTitle)
|
|
251
|
+
.font(.action2)
|
|
252
|
+
}.foregroundColor(Colors.black09)
|
|
253
|
+
}
|
|
254
|
+
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
255
|
+
.padding(EdgeInsets(top: 0, leading: 0, bottom: 24, trailing: 24))
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
var RowButtons: some View {
|
|
259
|
+
HStack {
|
|
260
|
+
if(!closeButtonTitle.isEmpty) {
|
|
261
|
+
Button(title: closeButtonTitle, action: onPressCloseButton, type: .text, size: .medium).accessibility(identifier: "btn_popup_cancel")
|
|
262
|
+
}
|
|
263
|
+
Button(title: actionButtonTitle, action: onPressAction, size: .medium)
|
|
264
|
+
.accessibility(identifier: "btn_popup_allow")
|
|
265
|
+
}
|
|
266
|
+
.frame(maxWidth: .infinity, alignment: .center)
|
|
267
|
+
.padding(.horizontal, 24)
|
|
268
|
+
.padding(.bottom, 24)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
var ColumnButtons: some View {
|
|
272
|
+
VStack(alignment: .trailing) {
|
|
273
|
+
Button(
|
|
274
|
+
title: actionButtonTitle,
|
|
275
|
+
action: onPressAction,
|
|
276
|
+
size: .medium
|
|
277
|
+
).accessibility(identifier: "btn_popup_allow")
|
|
278
|
+
if(!closeButtonTitle.isEmpty) {
|
|
279
|
+
Button(title: closeButtonTitle, action: onPressCloseButton, type: .text, size: .medium).accessibility(identifier: "btn_popup_cancel")
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
283
|
+
.padding(.horizontal, 24)
|
|
284
|
+
.padding(.bottom, 24)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import SDWebImageSwiftUI
|
|
2
|
+
import SwiftUI
|
|
3
|
+
|
|
4
|
+
public struct PopupInput: View {
|
|
5
|
+
// MARK: Lifecycle
|
|
6
|
+
|
|
7
|
+
public init(isPresented: Binding<Bool>, title: String = "", description: String = "", numberOfButtons: Int = 1, actionButtonTitle: String = "", closeButtonTitle: String = "", onPressAction: @escaping () -> Void = {}, onPressCloseButton: @escaping () -> Void = {}, onClose: @escaping () -> Void = {}, inputPlaceholder: String = "", inputValue: Binding<String> = Binding.constant(""), onTextChange: ((String) -> Void)? = nil) {
|
|
8
|
+
self._isPresented = isPresented
|
|
9
|
+
self._inputValue = inputValue
|
|
10
|
+
self.title = title
|
|
11
|
+
self.description = description
|
|
12
|
+
self.onPressAction = onPressAction
|
|
13
|
+
self.onPressCloseButton = onPressCloseButton
|
|
14
|
+
self.onClose = onClose
|
|
15
|
+
self.inputPlaceholder = inputPlaceholder
|
|
16
|
+
self.onTextChange = onTextChange
|
|
17
|
+
self.numberOfButtons = numberOfButtons
|
|
18
|
+
self.closeButtonTitle = closeButtonTitle
|
|
19
|
+
self.actionButtonTitle = actionButtonTitle
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// MARK: Public
|
|
23
|
+
|
|
24
|
+
public var body: some View {
|
|
25
|
+
VStack(spacing: 12) {
|
|
26
|
+
ZStack(alignment: .topTrailing) {
|
|
27
|
+
SwiftUI.Button(action: onPressClose) {
|
|
28
|
+
Image(systemName: "xmark.circle.fill")
|
|
29
|
+
.resizable()
|
|
30
|
+
.frame(width: 16, height: 16)
|
|
31
|
+
}.foregroundColor(Colors.black20)
|
|
32
|
+
.background(Colors.black01.cornerRadius(8))
|
|
33
|
+
.offset(x: 6, y: -6)
|
|
34
|
+
.zIndex(1)
|
|
35
|
+
|
|
36
|
+
VStack {
|
|
37
|
+
VStack(alignment: .leading) {
|
|
38
|
+
Text(title)
|
|
39
|
+
.foregroundColor(.black)
|
|
40
|
+
.font(.headerText1)
|
|
41
|
+
.padding(.top, 12)
|
|
42
|
+
.padding(.bottom, 24)
|
|
43
|
+
|
|
44
|
+
Text(description)
|
|
45
|
+
.foregroundColor(.black)
|
|
46
|
+
.font(.paragraph)
|
|
47
|
+
.opacity(0.6)
|
|
48
|
+
.multilineTextAlignment(.leading)
|
|
49
|
+
.padding(.bottom, 20)
|
|
50
|
+
}.padding(.horizontal, 24)
|
|
51
|
+
|
|
52
|
+
Input($inputValue, placeholder: inputPlaceholder, onChange: onTextChange)
|
|
53
|
+
.padding(.horizontal, 24)
|
|
54
|
+
|
|
55
|
+
if numberOfButtons == 1 {
|
|
56
|
+
Button(title: actionButtonTitle, action: onPressAction)
|
|
57
|
+
.padding(.horizontal, 24)
|
|
58
|
+
.padding(.bottom, 24)
|
|
59
|
+
} else {
|
|
60
|
+
HStack {
|
|
61
|
+
Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary)
|
|
62
|
+
Button(title: actionButtonTitle, action: onPressAction)
|
|
63
|
+
}.padding(.horizontal, 24)
|
|
64
|
+
.padding(.bottom, 24)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}.clipped()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
.background(Color.white.cornerRadius(8))
|
|
71
|
+
.shadow(color: .black.opacity(0.08), radius: 2, x: 0, y: 0)
|
|
72
|
+
.shadow(color: .black.opacity(0.16), radius: 24, x: 0, y: 0)
|
|
73
|
+
.padding(.horizontal, 40)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// MARK: Internal
|
|
77
|
+
|
|
78
|
+
@Binding var isPresented: Bool
|
|
79
|
+
@Binding var inputValue: String
|
|
80
|
+
var title: String
|
|
81
|
+
var description: String
|
|
82
|
+
var onPressAction: () -> Void
|
|
83
|
+
var onPressCloseButton: () -> Void
|
|
84
|
+
var onClose: () -> Void
|
|
85
|
+
var inputPlaceholder: String
|
|
86
|
+
var onTextChange: ((String) -> Void)?
|
|
87
|
+
var numberOfButtons: Int
|
|
88
|
+
var actionButtonTitle: String
|
|
89
|
+
var closeButtonTitle: String
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
func onPressClose(){
|
|
93
|
+
isPresented = false
|
|
94
|
+
onClose()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import SDWebImageSwiftUI
|
|
3
|
+
|
|
4
|
+
public struct PopupPromotion: View {
|
|
5
|
+
var imageUrl: String
|
|
6
|
+
var onIconClose: (() -> Void)?
|
|
7
|
+
var onAction: (() -> Void)?
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
public init(
|
|
11
|
+
imageUrl: String,
|
|
12
|
+
onIconClose: (() -> Void)? = nil,
|
|
13
|
+
onAction: (() -> Void)? = nil
|
|
14
|
+
) {
|
|
15
|
+
self.imageUrl = imageUrl
|
|
16
|
+
self.onIconClose = onIconClose
|
|
17
|
+
self.onAction = onAction
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public var body: some View {
|
|
21
|
+
VStack(spacing: 8) {
|
|
22
|
+
if(imageUrl.isEmpty) {
|
|
23
|
+
Icon(source: "media_fail", size: UIScreen.main.bounds.width - Spacing.XL * 2)
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
WebImage(url: URL(string: imageUrl)!)
|
|
27
|
+
.resizable()
|
|
28
|
+
.placeholder {
|
|
29
|
+
Color.gray
|
|
30
|
+
.aspectRatio(0.72, contentMode: .fit)
|
|
31
|
+
.frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2)
|
|
32
|
+
.clipped()
|
|
33
|
+
}
|
|
34
|
+
.scaledToFill()
|
|
35
|
+
.aspectRatio(0.72, contentMode: .fit)
|
|
36
|
+
.frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2, alignment: .center)
|
|
37
|
+
.clipped()
|
|
38
|
+
.onTapGesture {
|
|
39
|
+
onAction?()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
buildCloseIcon()
|
|
43
|
+
.accessibility(identifier: "ic_popup_close")
|
|
44
|
+
}
|
|
45
|
+
.accessibilityElement(children: .ignore)
|
|
46
|
+
.accessibility(identifier: "popup_promotion")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@ViewBuilder
|
|
50
|
+
private func buildCloseIcon() -> some View {
|
|
51
|
+
HStack(alignment: .center, spacing: 0) {
|
|
52
|
+
SwiftUI.Button(action: {
|
|
53
|
+
onIconClose?()
|
|
54
|
+
}) {
|
|
55
|
+
ZStack {
|
|
56
|
+
Circle()
|
|
57
|
+
.strokeBorder(Colors.black01, lineWidth: 2)
|
|
58
|
+
.background(Circle().fill(Colors.black17))
|
|
59
|
+
.frame(width: 22, height: 22)
|
|
60
|
+
|
|
61
|
+
Icon(source: "navigation_close", size: 16, color: Colors.black01)
|
|
62
|
+
}
|
|
63
|
+
.frame(width: 40, height: 40)
|
|
64
|
+
.padding(8)
|
|
65
|
+
.zIndex(1)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|