@momo-kits/native-kits 0.111.1-rc.2 → 0.112.1-rc.1
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/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +6 -6
- package/ios/Application/Components.swift +223 -0
- package/ios/Application/FloatingButton.swift +148 -0
- package/ios/Application/Screen.swift +175 -0
- package/ios/Checkbox/Checkbox.swift +1 -1
- package/ios/Input/InputSearch.swift +56 -0
- package/package.json +1 -1
|
@@ -39,15 +39,15 @@ fun getFont(font: String): FontFamily {
|
|
|
39
39
|
fun getFontFamily(fontFamily: String, fontWeight: FontWeight? = FontWeight.Normal): FontFamily {
|
|
40
40
|
val key = "$fontFamily-${fontWeight?.weight}"
|
|
41
41
|
val fontMap = mapOf(
|
|
42
|
-
"SFProText-100" to "sfprotext_thin.
|
|
43
|
-
"SFProText-200" to "sfprotext_ultralight.
|
|
42
|
+
"SFProText-100" to "sfprotext_thin.otf",
|
|
43
|
+
"SFProText-200" to "sfprotext_ultralight.otf",
|
|
44
44
|
"SFProText-300" to "sfprotext_light.ttf",
|
|
45
45
|
"SFProText-400" to "sfprotext_regular.ttf",
|
|
46
46
|
"SFProText-500" to "sfprotext_medium.ttf",
|
|
47
|
-
"SFProText-600" to "
|
|
48
|
-
"SFProText-700" to "
|
|
49
|
-
"SFProText-800" to "
|
|
50
|
-
"SFProText-900" to "
|
|
47
|
+
"SFProText-600" to "sfprotext_semibold.ttf",
|
|
48
|
+
"SFProText-700" to "sfprotext_bold.ttf",
|
|
49
|
+
"SFProText-800" to "sfprotext_heavy.ttf",
|
|
50
|
+
"SFProText-900" to "sfprotext_black.otf",
|
|
51
51
|
|
|
52
52
|
"MoMoSignature-100" to "momosignature.otf",
|
|
53
53
|
"MoMoSignature-200" to "momosignature.otf",
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Components.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by sophia on 24/1/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import SwiftUI
|
|
9
|
+
|
|
10
|
+
public enum TitlePosition {
|
|
11
|
+
case left, center
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public enum HeaderType {
|
|
15
|
+
case `default`, extended, none
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
public struct AnimatedHeader {
|
|
20
|
+
public var type: HeaderType = .default
|
|
21
|
+
public var composable: (_ scrollState: Int) -> AnyView = { _ in AnyView(EmptyView()) }
|
|
22
|
+
|
|
23
|
+
public init(
|
|
24
|
+
type: HeaderType = .default,
|
|
25
|
+
composable: @escaping (_ scrollState: Int) -> AnyView = { _ in AnyView(EmptyView()) }
|
|
26
|
+
) {
|
|
27
|
+
self.type = type
|
|
28
|
+
self.composable = composable
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func safeAreaTopInset() -> CGFloat {
|
|
33
|
+
if #available(iOS 15.0, *) {
|
|
34
|
+
return UIApplication.shared.connectedScenes
|
|
35
|
+
.compactMap { ($0 as? UIWindowScene)?.keyWindow?.safeAreaInsets.top }
|
|
36
|
+
.first ?? 0
|
|
37
|
+
} else {
|
|
38
|
+
return UIApplication.shared.connectedScenes
|
|
39
|
+
.compactMap { ($0 as? UIWindowScene)?.windows.first?.safeAreaInsets.top }
|
|
40
|
+
.first ?? 0
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
public struct HeaderBackground: View {
|
|
46
|
+
var headerType: HeaderType = .default
|
|
47
|
+
var scrollState: CGFloat
|
|
48
|
+
|
|
49
|
+
public var body: some View {
|
|
50
|
+
|
|
51
|
+
let statusBarHeight = safeAreaTopInset()
|
|
52
|
+
|
|
53
|
+
let opacity = 1 - CGFloat(scrollState) / 200
|
|
54
|
+
let backgroundColor = scrollState == 0 ? Color.clear : Colors.black01
|
|
55
|
+
|
|
56
|
+
Group {
|
|
57
|
+
if headerType == .default {
|
|
58
|
+
let height = statusBarHeight + 52
|
|
59
|
+
ZStack {
|
|
60
|
+
|
|
61
|
+
Rectangle()
|
|
62
|
+
.fill(backgroundColor)
|
|
63
|
+
.frame(height: height)
|
|
64
|
+
.shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
|
|
65
|
+
.overlay(Rectangle()
|
|
66
|
+
.fill(LinearGradient(
|
|
67
|
+
gradient: Gradient(colors: [
|
|
68
|
+
Color(red: 1, green: 0.8, blue: 0.87),
|
|
69
|
+
Color(red: 1, green: 0.8, blue: 0.87).opacity(0.5)
|
|
70
|
+
]),
|
|
71
|
+
startPoint: .top,
|
|
72
|
+
endPoint: .bottom))
|
|
73
|
+
.opacity(opacity)
|
|
74
|
+
.frame(height: height))
|
|
75
|
+
.overlay(Rectangle()
|
|
76
|
+
.fill(Colors.black04)
|
|
77
|
+
.frame(height: 1)
|
|
78
|
+
.frame(maxWidth: .infinity)
|
|
79
|
+
.alignmentGuide(.bottom) { _ in 0 }, alignment: .bottom
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
.frame(maxWidth: .infinity)
|
|
83
|
+
|
|
84
|
+
} else if headerType == .extended {
|
|
85
|
+
ZStack {
|
|
86
|
+
Rectangle()
|
|
87
|
+
.fill(LinearGradient(
|
|
88
|
+
gradient: Gradient(colors: [
|
|
89
|
+
Color(red: 1, green: 0.8, blue: 0.87),
|
|
90
|
+
Color(red: 1, green: 0.8, blue: 0.87).opacity(0)
|
|
91
|
+
]),
|
|
92
|
+
startPoint: .top,
|
|
93
|
+
endPoint: .bottom))
|
|
94
|
+
.opacity(opacity)
|
|
95
|
+
.frame(height: 154)
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public struct Header: View {
|
|
105
|
+
var headerType: HeaderType = .default
|
|
106
|
+
var titlePosition: TitlePosition
|
|
107
|
+
var title: String
|
|
108
|
+
var headerRight: (() -> AnyView)?
|
|
109
|
+
var goBack: (() -> Void)?
|
|
110
|
+
var opacity: CGFloat? = nil
|
|
111
|
+
var animatedHeader: AnimatedHeader? = nil
|
|
112
|
+
var scrollState: CGFloat = 0
|
|
113
|
+
var inputSearchProps: InputSearchProps? = nil
|
|
114
|
+
|
|
115
|
+
@State private var colorFraction: CGFloat = 0
|
|
116
|
+
|
|
117
|
+
public init(
|
|
118
|
+
headerType: HeaderType = .default,
|
|
119
|
+
titlePosition: TitlePosition,
|
|
120
|
+
title: String,
|
|
121
|
+
headerRight: (() -> AnyView)? = nil,
|
|
122
|
+
goBack: (() -> Void)? = nil,
|
|
123
|
+
opacity: CGFloat? = nil,
|
|
124
|
+
animatedHeader: AnimatedHeader? = nil,
|
|
125
|
+
scrollState: CGFloat = 0,
|
|
126
|
+
inputSearchProps: InputSearchProps? = nil
|
|
127
|
+
) {
|
|
128
|
+
self.headerType = headerType
|
|
129
|
+
self.titlePosition = titlePosition
|
|
130
|
+
self.title = title
|
|
131
|
+
self.headerRight = headerRight
|
|
132
|
+
self.goBack = goBack
|
|
133
|
+
self.opacity = opacity
|
|
134
|
+
self.animatedHeader = animatedHeader
|
|
135
|
+
self.scrollState = scrollState
|
|
136
|
+
self.inputSearchProps = inputSearchProps
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public var body: some View {
|
|
140
|
+
|
|
141
|
+
let statusBarHeight = safeAreaTopInset()
|
|
142
|
+
let tintIconColor = Colors.black17
|
|
143
|
+
let backgroundButton = Colors.black01.opacity(0.6)
|
|
144
|
+
|
|
145
|
+
if headerType != .none {
|
|
146
|
+
VStack(spacing: 0) {
|
|
147
|
+
ZStack {
|
|
148
|
+
HStack {
|
|
149
|
+
if let goBack = goBack {
|
|
150
|
+
SwiftUI.Button(action: goBack) {
|
|
151
|
+
Circle()
|
|
152
|
+
.stroke(Color.black.opacity(0.2), lineWidth: 0.2)
|
|
153
|
+
.background(Circle().fill(backgroundButton))
|
|
154
|
+
.frame(width: 28, height: 28)
|
|
155
|
+
.overlay(
|
|
156
|
+
Image(systemName: "arrow.backward")
|
|
157
|
+
.foregroundColor(.black)
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Spacer() // Pushes headerRight to the right
|
|
164
|
+
|
|
165
|
+
if let headerRight = headerRight {
|
|
166
|
+
headerRight()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
.frame(height: 52)
|
|
170
|
+
.padding(.horizontal)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
HStack {
|
|
175
|
+
if titlePosition == .left, goBack != nil {
|
|
176
|
+
Spacer().frame(width: 40)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if inputSearchProps != nil {
|
|
180
|
+
if let inputProps = inputSearchProps {
|
|
181
|
+
InputSearch(
|
|
182
|
+
text: inputProps.$text,
|
|
183
|
+
buttonText: inputProps.buttonText,
|
|
184
|
+
showButtonText: inputProps.showButtonText,
|
|
185
|
+
showBorder: inputProps.showBorder,
|
|
186
|
+
placeholder: inputProps.placeholder,
|
|
187
|
+
onChangeText: inputProps.onChangeText,
|
|
188
|
+
onPressButtonText: inputProps.onPressButtonText,
|
|
189
|
+
error: inputProps.error,
|
|
190
|
+
disabled: inputProps.disabled,
|
|
191
|
+
icon: inputProps.icon,
|
|
192
|
+
iconColor: inputProps.iconColor,
|
|
193
|
+
onRightIconPressed: inputProps.onRightIconPressed,
|
|
194
|
+
onFocus: inputProps.onFocus,
|
|
195
|
+
onBlur: inputProps.onBlur,
|
|
196
|
+
loading: inputProps.loading,
|
|
197
|
+
fontWeight: inputProps.fontWeight,
|
|
198
|
+
keyboardType: inputProps.keyboardType
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
Text(title)
|
|
203
|
+
.font(.headline)
|
|
204
|
+
.foregroundColor(tintIconColor)
|
|
205
|
+
.frame(maxWidth: titlePosition == .center ? .infinity : nil, alignment: titlePosition == .center ? .center : .leading)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Spacer()
|
|
209
|
+
}
|
|
210
|
+
.frame(height: 52)
|
|
211
|
+
.padding(.horizontal)
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
.background(Color.clear)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FloatingButton.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by sophia on 6/2/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import SwiftUI
|
|
9
|
+
import Combine
|
|
10
|
+
|
|
11
|
+
public enum FABSize {
|
|
12
|
+
case small
|
|
13
|
+
case `default`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public enum FABPosition {
|
|
17
|
+
case end
|
|
18
|
+
case center
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public struct FabProps {
|
|
22
|
+
let icon: String
|
|
23
|
+
let iconColor: Color?
|
|
24
|
+
let label: String?
|
|
25
|
+
let onClick: () -> Void
|
|
26
|
+
let size: FABSize
|
|
27
|
+
let bottom: CGFloat?
|
|
28
|
+
let scrollOffset: CGFloat?
|
|
29
|
+
let position: FABPosition?
|
|
30
|
+
|
|
31
|
+
public init(
|
|
32
|
+
icon: String,
|
|
33
|
+
iconColor: Color? = nil,
|
|
34
|
+
label: String? = nil,
|
|
35
|
+
onClick: @escaping () -> Void,
|
|
36
|
+
size: FABSize = .default,
|
|
37
|
+
bottom: CGFloat? = nil,
|
|
38
|
+
scrollOffset: CGFloat? = nil,
|
|
39
|
+
position: FABPosition? = .end
|
|
40
|
+
) {
|
|
41
|
+
self.icon = icon
|
|
42
|
+
self.iconColor = iconColor
|
|
43
|
+
self.label = label
|
|
44
|
+
self.onClick = onClick
|
|
45
|
+
self.size = size
|
|
46
|
+
self.bottom = bottom
|
|
47
|
+
self.scrollOffset = scrollOffset
|
|
48
|
+
self.position = position
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public struct FloatingButton: View {
|
|
53
|
+
@State private var lastScrollOffset: CGFloat = 0
|
|
54
|
+
@State private var isExpanded: Bool = false
|
|
55
|
+
let scrollOffset: CGFloat
|
|
56
|
+
let bottomPadding: CGFloat
|
|
57
|
+
let onClick: () -> Void
|
|
58
|
+
let containerColor: Color
|
|
59
|
+
let contentColor: Color
|
|
60
|
+
let icon: String
|
|
61
|
+
let iconColor: Color?
|
|
62
|
+
let text: String?
|
|
63
|
+
let size: FABSize
|
|
64
|
+
let position: FABPosition
|
|
65
|
+
let keyboardOffset: CGFloat
|
|
66
|
+
|
|
67
|
+
public init(
|
|
68
|
+
scrollOffset: CGFloat = 0,
|
|
69
|
+
bottomPadding: CGFloat = 0,
|
|
70
|
+
onClick: @escaping () -> Void,
|
|
71
|
+
containerColor: Color = .blue,
|
|
72
|
+
contentColor: Color = .white,
|
|
73
|
+
icon: String,
|
|
74
|
+
iconColor: Color? = nil,
|
|
75
|
+
text: String? = nil,
|
|
76
|
+
size: FABSize = .default,
|
|
77
|
+
position: FABPosition = .end,
|
|
78
|
+
keyboardOffset: CGFloat = 0
|
|
79
|
+
) {
|
|
80
|
+
self.scrollOffset = scrollOffset
|
|
81
|
+
self.bottomPadding = bottomPadding
|
|
82
|
+
self.onClick = onClick
|
|
83
|
+
self.containerColor = containerColor
|
|
84
|
+
self.contentColor = contentColor
|
|
85
|
+
self.icon = icon
|
|
86
|
+
self.iconColor = iconColor
|
|
87
|
+
self.text = text
|
|
88
|
+
self.size = size
|
|
89
|
+
self.position = position
|
|
90
|
+
self.keyboardOffset = keyboardOffset
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public var body: some View {
|
|
94
|
+
VStack {
|
|
95
|
+
Spacer()
|
|
96
|
+
HStack {
|
|
97
|
+
if position == .center {
|
|
98
|
+
Spacer()
|
|
99
|
+
}
|
|
100
|
+
FloatingButtonContent()
|
|
101
|
+
.padding(.trailing, position == .end ? 12 : 0)
|
|
102
|
+
.padding(.bottom, bottomPadding)
|
|
103
|
+
.offset(y: -keyboardOffset)
|
|
104
|
+
if position == .end {
|
|
105
|
+
Spacer()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
.onReceive(Just(scrollOffset)) { newValue in
|
|
110
|
+
withAnimation(.easeInOut(duration: 0.3)) {
|
|
111
|
+
isExpanded = newValue > lastScrollOffset
|
|
112
|
+
lastScrollOffset = newValue
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@ViewBuilder
|
|
118
|
+
private func FloatingButtonContent() -> some View {
|
|
119
|
+
SwiftUI.Button(action: onClick) {
|
|
120
|
+
HStack {
|
|
121
|
+
Image(systemName: icon)
|
|
122
|
+
.foregroundColor(iconColor ?? .white)
|
|
123
|
+
.font(size == .small ? .system(size: 12) : .system(size: 24))
|
|
124
|
+
|
|
125
|
+
if isExpanded, let text = text {
|
|
126
|
+
Text(text)
|
|
127
|
+
.foregroundColor(.white)
|
|
128
|
+
.transition(.asymmetric(insertion: expandAnimation, removal: collapseAnimation))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
.padding(.horizontal, isExpanded ? 12 : 0)
|
|
132
|
+
.frame(width: isExpanded ? 80 : (size == .small ? 36 : 48), height: size == .small ? 36 : 48)
|
|
133
|
+
.background(containerColor)
|
|
134
|
+
.cornerRadius(24)
|
|
135
|
+
.shadow(radius: 4)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private var expandAnimation: AnyTransition {
|
|
140
|
+
AnyTransition.opacity.animation(.easeIn(duration: 0.2))
|
|
141
|
+
.combined(with: .move(edge: .leading))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private var collapseAnimation: AnyTransition {
|
|
145
|
+
AnyTransition.opacity.animation(.easeOut(duration: 0.5))
|
|
146
|
+
.combined(with: .move(edge: .trailing))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Screen.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by sophia on 5/2/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import SwiftUI
|
|
9
|
+
|
|
10
|
+
public struct Screen<Content: View>: View {
|
|
11
|
+
var backgroundColor: Color?
|
|
12
|
+
var isBack: Bool
|
|
13
|
+
var headerType: HeaderType
|
|
14
|
+
var verticalAlignment: VerticalAlignment
|
|
15
|
+
var horizontalAlignment: HorizontalAlignment
|
|
16
|
+
var title: String
|
|
17
|
+
var titlePosition: TitlePosition
|
|
18
|
+
var goBack: (() -> Void)?
|
|
19
|
+
var scrollable: Bool
|
|
20
|
+
var onContentLayout: ((CGRect) -> Void)?
|
|
21
|
+
var useAvoidKeyboard: Bool
|
|
22
|
+
var footer: (() -> AnyView)?
|
|
23
|
+
var headerRight: (() -> AnyView)?
|
|
24
|
+
var animatedHeader: AnimatedHeader? = nil
|
|
25
|
+
var layoutOffset: CGFloat
|
|
26
|
+
var inputSearchProps: InputSearchProps? = nil
|
|
27
|
+
var fabProps: FabProps? = nil
|
|
28
|
+
var content: () -> Content
|
|
29
|
+
|
|
30
|
+
public init(
|
|
31
|
+
backgroundColor: Color? = nil,
|
|
32
|
+
isBack: Bool = true,
|
|
33
|
+
headerType: HeaderType = .default,
|
|
34
|
+
verticalAlignment: VerticalAlignment = .top,
|
|
35
|
+
horizontalAlignment: HorizontalAlignment = .leading,
|
|
36
|
+
title: String = "Stack",
|
|
37
|
+
titlePosition: TitlePosition = .center,
|
|
38
|
+
goBack: (() -> Void)? = nil,
|
|
39
|
+
scrollable: Bool = true,
|
|
40
|
+
onContentLayout: ((CGRect) -> Void)? = nil,
|
|
41
|
+
useAvoidKeyboard: Bool = true,
|
|
42
|
+
footer: (() -> AnyView)? = nil,
|
|
43
|
+
headerRight: (() -> AnyView)? = nil,
|
|
44
|
+
animatedHeader: AnimatedHeader? = nil,
|
|
45
|
+
layoutOffset: CGFloat = 56,
|
|
46
|
+
inputSearchProps: InputSearchProps? = nil,
|
|
47
|
+
fabProps: FabProps? = nil,
|
|
48
|
+
@ViewBuilder content: @escaping () -> Content
|
|
49
|
+
) {
|
|
50
|
+
self.backgroundColor = backgroundColor
|
|
51
|
+
self.isBack = isBack
|
|
52
|
+
self.headerType = headerType
|
|
53
|
+
self.verticalAlignment = verticalAlignment
|
|
54
|
+
self.horizontalAlignment = horizontalAlignment
|
|
55
|
+
self.title = title
|
|
56
|
+
self.titlePosition = titlePosition
|
|
57
|
+
self.goBack = goBack
|
|
58
|
+
self.scrollable = scrollable
|
|
59
|
+
self.onContentLayout = onContentLayout
|
|
60
|
+
self.useAvoidKeyboard = useAvoidKeyboard
|
|
61
|
+
self.footer = footer
|
|
62
|
+
self.headerRight = headerRight
|
|
63
|
+
self.animatedHeader = animatedHeader
|
|
64
|
+
self.layoutOffset = layoutOffset
|
|
65
|
+
self.inputSearchProps = inputSearchProps
|
|
66
|
+
self.fabProps = fabProps
|
|
67
|
+
self.content = content
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@State private var scrollOffset: CGFloat = 0
|
|
71
|
+
|
|
72
|
+
public var body: some View {
|
|
73
|
+
GeometryReader { geometry in
|
|
74
|
+
let safeAreaInsets = geometry.safeAreaInsets
|
|
75
|
+
let statusBarHeight = safeAreaInsets.top
|
|
76
|
+
let keyboardHeight: CGFloat = 0
|
|
77
|
+
let keyboardSize: CGFloat = useAvoidKeyboard ? max(keyboardHeight, 0) : 0
|
|
78
|
+
|
|
79
|
+
ZStack {
|
|
80
|
+
(backgroundColor ?? Color.white).edgesIgnoringSafeArea(.all)
|
|
81
|
+
|
|
82
|
+
VStack(spacing: 0) {
|
|
83
|
+
ZStack {
|
|
84
|
+
if animatedHeader == nil {
|
|
85
|
+
HeaderBackground(headerType: headerType, scrollState: scrollOffset)
|
|
86
|
+
} else {
|
|
87
|
+
Color.white.opacity(Double(min(scrollOffset / 114, 1)))
|
|
88
|
+
.overlay(HeaderBackground(headerType: headerType, scrollState: scrollOffset))
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Header(
|
|
93
|
+
headerType: headerType,
|
|
94
|
+
titlePosition: titlePosition,
|
|
95
|
+
title: title,
|
|
96
|
+
headerRight: headerRight,
|
|
97
|
+
goBack: goBack,
|
|
98
|
+
opacity: min(scrollOffset / 114, 1),
|
|
99
|
+
animatedHeader: animatedHeader,
|
|
100
|
+
scrollState: scrollOffset,
|
|
101
|
+
inputSearchProps: inputSearchProps
|
|
102
|
+
).padding(.bottom, headerType == .extended ? 0 : -safeAreaTopInset())
|
|
103
|
+
}.padding(.top, -safeAreaTopInset())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
VStack(alignment: horizontalAlignment, spacing: 0) {
|
|
107
|
+
if animatedHeader != nil {
|
|
108
|
+
Spacer().frame(height: statusBarHeight + 52 + layoutOffset)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if scrollable {
|
|
112
|
+
ScrollView(.vertical, showsIndicators: false) {
|
|
113
|
+
VStack {
|
|
114
|
+
content()
|
|
115
|
+
}
|
|
116
|
+
.background(GeometryReader { geo -> Color in
|
|
117
|
+
DispatchQueue.main.async {
|
|
118
|
+
self.scrollOffset = -geo.frame(in: .global).origin.y
|
|
119
|
+
}
|
|
120
|
+
return Color.clear
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
content()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
.padding(.bottom, keyboardSize)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if let footerView = footer?() {
|
|
131
|
+
ZStack(alignment: .top) {
|
|
132
|
+
Rectangle()
|
|
133
|
+
.fill(Colors.black20)
|
|
134
|
+
.frame(height: 4)
|
|
135
|
+
.shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
|
|
136
|
+
|
|
137
|
+
Footer(footerView: footerView, keyboardSize: keyboardSize)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if let fabProps = fabProps {
|
|
142
|
+
FloatingButton(
|
|
143
|
+
scrollOffset: scrollOffset,
|
|
144
|
+
bottomPadding: fabProps.bottom ?? 16,
|
|
145
|
+
onClick: fabProps.onClick,
|
|
146
|
+
containerColor: .blue,
|
|
147
|
+
contentColor: .white,
|
|
148
|
+
icon: fabProps.icon,
|
|
149
|
+
iconColor: fabProps.iconColor,
|
|
150
|
+
text: fabProps.label,
|
|
151
|
+
size: fabProps.size,
|
|
152
|
+
position: fabProps.position ?? .end,
|
|
153
|
+
keyboardOffset: keyboardSize
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
struct Footer: View {
|
|
164
|
+
var footerView: AnyView
|
|
165
|
+
var keyboardSize: CGFloat
|
|
166
|
+
|
|
167
|
+
var body: some View {
|
|
168
|
+
VStack {
|
|
169
|
+
footerView
|
|
170
|
+
}
|
|
171
|
+
.padding(.bottom, keyboardSize)
|
|
172
|
+
.background(Colors.black01)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
@@ -37,6 +37,62 @@ struct RenderRightIconSearch: View {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
public struct InputSearchProps {
|
|
41
|
+
@Binding var text: String
|
|
42
|
+
var buttonText: String = ""
|
|
43
|
+
var showButtonText: Bool = false
|
|
44
|
+
var showBorder: Bool = true
|
|
45
|
+
var placeholder: String = ""
|
|
46
|
+
var onChangeText: (String) -> Void = { _ in }
|
|
47
|
+
var onPressButtonText: () -> Void = {}
|
|
48
|
+
var error: String = ""
|
|
49
|
+
var disabled: Bool = false
|
|
50
|
+
var icon: String = ""
|
|
51
|
+
var iconColor: Color = Colors.text
|
|
52
|
+
var onRightIconPressed: () -> Void = {}
|
|
53
|
+
var onFocus: () -> Void = {}
|
|
54
|
+
var onBlur: () -> Void = {}
|
|
55
|
+
var loading: Bool = false
|
|
56
|
+
var fontWeight: Font.Weight = .regular
|
|
57
|
+
var keyboardType: UIKeyboardType = .default
|
|
58
|
+
|
|
59
|
+
public init(text: Binding<String>,
|
|
60
|
+
buttonText: String = "",
|
|
61
|
+
showButtonText: Bool = false,
|
|
62
|
+
showBorder: Bool = true,
|
|
63
|
+
placeholder: String = "",
|
|
64
|
+
onChangeText: @escaping (String) -> Void = { _ in },
|
|
65
|
+
onPressButtonText: @escaping () -> Void = {},
|
|
66
|
+
error: String = "",
|
|
67
|
+
disabled: Bool = false,
|
|
68
|
+
icon: String = "",
|
|
69
|
+
iconColor: Color = Colors.text,
|
|
70
|
+
onRightIconPressed: @escaping () -> Void = {},
|
|
71
|
+
onFocus: @escaping () -> Void = {},
|
|
72
|
+
onBlur: @escaping () -> Void = {},
|
|
73
|
+
loading: Bool = false,
|
|
74
|
+
fontWeight: Font.Weight = .regular,
|
|
75
|
+
keyboardType: UIKeyboardType = .default) {
|
|
76
|
+
self._text = text
|
|
77
|
+
self.buttonText = buttonText
|
|
78
|
+
self.showButtonText = showButtonText
|
|
79
|
+
self.showBorder = showBorder
|
|
80
|
+
self.placeholder = placeholder
|
|
81
|
+
self.onChangeText = onChangeText
|
|
82
|
+
self.onPressButtonText = onPressButtonText
|
|
83
|
+
self.error = error
|
|
84
|
+
self.disabled = disabled
|
|
85
|
+
self.icon = icon
|
|
86
|
+
self.iconColor = iconColor
|
|
87
|
+
self.onRightIconPressed = onRightIconPressed
|
|
88
|
+
self.onFocus = onFocus
|
|
89
|
+
self.onBlur = onBlur
|
|
90
|
+
self.loading = loading
|
|
91
|
+
self.fontWeight = fontWeight
|
|
92
|
+
self.keyboardType = keyboardType
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
40
96
|
// MARK: - InputSearch
|
|
41
97
|
|
|
42
98
|
public struct InputSearch: View {
|