@momo-kits/native-kits 0.110.1-beta.1 → 0.110.1-beta.30
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/build.gradle.kts +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Components.kt +23 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +53 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +8 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +19 -15
- package/ios/Checkbox/Checkbox.swift +17 -80
- package/ios/Input/Input.swift +44 -144
- package/local.properties +2 -2
- package/package.json +1 -1
- package/ios/Input/InputSearch.swift +0 -182
- package/ios/Input/InputTextArea.swift +0 -242
package/compose/build.gradle.kts
CHANGED
|
@@ -3,6 +3,7 @@ plugins {
|
|
|
3
3
|
alias(libs.plugins.jetbrains.kotlin.multiplatform)
|
|
4
4
|
alias(libs.plugins.compose)
|
|
5
5
|
alias(libs.plugins.kotlin.compose.compiler)
|
|
6
|
+
kotlin("plugin.serialization")
|
|
6
7
|
id("maven-publish")
|
|
7
8
|
}
|
|
8
9
|
|
|
@@ -39,6 +40,7 @@ kotlin {
|
|
|
39
40
|
implementation(libs.coil.multiplatform.compose)
|
|
40
41
|
implementation(libs.coil.multiplatform.core)
|
|
41
42
|
implementation(libs.coil.multiplatform.network.ktor)
|
|
43
|
+
implementation(libs.jetbrains.serialization.json)
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
val androidMain by getting {
|
|
@@ -73,11 +73,19 @@ fun HeaderBackground(
|
|
|
73
73
|
targetValue = (1 - (scrollState / 200f)).coerceIn(0f, 1f),
|
|
74
74
|
)
|
|
75
75
|
|
|
76
|
+
val backgroundColor = if (scrollState == 0) {
|
|
77
|
+
Color.Transparent
|
|
78
|
+
} else {
|
|
79
|
+
AppTheme.current.colors.background.surface
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
76
83
|
when (headerType) {
|
|
77
84
|
HeaderType.DEFAULT -> {
|
|
78
85
|
val height = statusBarHeight + 52.dp
|
|
79
86
|
Box(
|
|
80
87
|
modifier = Modifier.fillMaxWidth().height(height)
|
|
88
|
+
.background(backgroundColor)
|
|
81
89
|
.bottomBorder(
|
|
82
90
|
strokeWidth = 1.dp,
|
|
83
91
|
color = AppTheme.current.colors.border.default
|
|
@@ -101,6 +109,7 @@ fun HeaderBackground(
|
|
|
101
109
|
)
|
|
102
110
|
if (AppTheme.current.assets.headerBackground != null) {
|
|
103
111
|
Image(
|
|
112
|
+
loading = false,
|
|
104
113
|
source = AppTheme.current.assets.headerBackground!!,
|
|
105
114
|
modifier = Modifier.fillMaxWidth().height(height),
|
|
106
115
|
options = Options(
|
|
@@ -113,6 +122,7 @@ fun HeaderBackground(
|
|
|
113
122
|
}
|
|
114
123
|
|
|
115
124
|
HeaderType.EXTENDED -> {
|
|
125
|
+
|
|
116
126
|
Box(
|
|
117
127
|
modifier = Modifier.fillMaxWidth().height(154.dp)
|
|
118
128
|
.alpha(opacityAni)
|
|
@@ -128,13 +138,19 @@ fun HeaderBackground(
|
|
|
128
138
|
)
|
|
129
139
|
) {
|
|
130
140
|
if (AppTheme.current.assets.headerBackground != null) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
141
|
+
Box(
|
|
142
|
+
modifier = Modifier.fillMaxWidth()
|
|
143
|
+
.background(backgroundColor)
|
|
144
|
+
) {
|
|
145
|
+
Image(
|
|
146
|
+
loading = false,
|
|
147
|
+
source = AppTheme.current.assets.headerBackground!!,
|
|
148
|
+
modifier = Modifier.fillMaxWidth().height(154.dp),
|
|
149
|
+
options = Options(
|
|
150
|
+
contentScale = ContentScale.FillWidth
|
|
151
|
+
)
|
|
136
152
|
)
|
|
137
|
-
|
|
153
|
+
}
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
156
|
}
|
|
@@ -246,7 +262,7 @@ fun Header(
|
|
|
246
262
|
}
|
|
247
263
|
if (inputSearchProps != null) {
|
|
248
264
|
InputSearch(inputSearchProps = inputSearchProps.copy(backgroundColor = backgroundSearch))
|
|
249
|
-
} else{
|
|
265
|
+
} else {
|
|
250
266
|
Box(
|
|
251
267
|
Modifier.weight(1f).graphicsLayer {
|
|
252
268
|
alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
|
|
@@ -1,64 +1,77 @@
|
|
|
1
1
|
package vn.momo.kits.application
|
|
2
2
|
|
|
3
|
-
import androidx.compose.foundation.layout.WindowInsets
|
|
4
|
-
import androidx.compose.foundation.layout.asPaddingValues
|
|
5
|
-
import androidx.compose.foundation.layout.systemBars
|
|
6
3
|
import androidx.compose.runtime.Composable
|
|
7
4
|
import androidx.compose.runtime.CompositionLocalProvider
|
|
5
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
6
|
+
import androidx.compose.runtime.getValue
|
|
7
|
+
import androidx.compose.runtime.mutableStateOf
|
|
8
|
+
import androidx.compose.runtime.remember
|
|
9
|
+
import androidx.compose.runtime.rememberCoroutineScope
|
|
10
|
+
import androidx.compose.runtime.setValue
|
|
8
11
|
import androidx.compose.runtime.staticCompositionLocalOf
|
|
12
|
+
import androidx.compose.ui.unit.Dp
|
|
13
|
+
import kotlinx.coroutines.launch
|
|
14
|
+
import kotlinx.serialization.json.Json
|
|
15
|
+
import kotlinx.serialization.json.jsonObject
|
|
16
|
+
import kotlinx.serialization.json.jsonPrimitive
|
|
9
17
|
import vn.momo.kits.const.AppStatusBar
|
|
10
18
|
import vn.momo.kits.const.AppTheme
|
|
11
19
|
import vn.momo.kits.const.Theme
|
|
20
|
+
import vn.momo.kits.const.ThemeAssets
|
|
12
21
|
import vn.momo.kits.const.defaultTheme
|
|
13
22
|
import vn.momo.kits.platform.disableOverscroll
|
|
14
23
|
import vn.momo.kits.platform.getStatusBarHeight
|
|
15
24
|
|
|
16
|
-
val
|
|
17
|
-
error("no navigator provided!")
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
class Navigator {
|
|
21
|
-
fun push(content: @Composable () -> Unit) {
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
fun present(content: @Composable () -> Unit) {
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
fun reset(content: @Composable () -> Unit) {
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
fun pop() {
|
|
33
|
-
}
|
|
25
|
+
val PlatformApi = staticCompositionLocalOf<Any?> { null }
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
fun
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
fun showModal(
|
|
42
|
-
onClose: () -> Unit = {},
|
|
43
|
-
canBackgroundClose: Boolean = true,
|
|
44
|
-
content: @Composable () -> Unit,
|
|
45
|
-
) {
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
fun showBottomSheet(content: @Composable () -> Unit) {
|
|
49
|
-
}
|
|
27
|
+
interface ComposeApi {
|
|
28
|
+
fun request(funcName: String, params: String?): String
|
|
29
|
+
fun request(funcName: String, params: String?, onResponse: ((String) -> Unit)?): String
|
|
30
|
+
fun requestCallback(funcName: String, params: String?, onResponse: ((String) -> Unit)?)
|
|
31
|
+
fun removeCallback(id: String)
|
|
50
32
|
}
|
|
51
33
|
|
|
52
34
|
@Composable
|
|
53
35
|
fun ApplicationContainer(
|
|
54
36
|
theme: Theme = defaultTheme,
|
|
55
|
-
|
|
37
|
+
composeApi: ComposeApi? = null,
|
|
38
|
+
statusBarHeight: Dp? = AppStatusBar.current ?: getStatusBarHeight(),
|
|
39
|
+
content: @Composable () -> Unit
|
|
56
40
|
) {
|
|
41
|
+
var appTheme by remember { mutableStateOf(theme) }
|
|
42
|
+
val coroutineScope = rememberCoroutineScope()
|
|
43
|
+
|
|
44
|
+
LaunchedEffect(Unit) {
|
|
45
|
+
try {
|
|
46
|
+
composeApi?.request("getConfig", "DESIGN_SYSTEM") {
|
|
47
|
+
val json = Json { ignoreUnknownKeys = true }
|
|
48
|
+
val response = json.parseToJsonElement(it).jsonObject["response"]
|
|
49
|
+
val headerBar = response
|
|
50
|
+
?.jsonObject
|
|
51
|
+
?.get("headerBar")
|
|
52
|
+
?.jsonPrimitive
|
|
53
|
+
?.content
|
|
54
|
+
if (headerBar != null && appTheme.assets.headerBackground == null) {
|
|
55
|
+
coroutineScope.launch {
|
|
56
|
+
appTheme = appTheme.copy(
|
|
57
|
+
assets = ThemeAssets(
|
|
58
|
+
headerBackground = headerBar
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (e: Exception) {
|
|
66
|
+
print("@@ == NavigationContainer fetch config error $e")
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
disableOverscroll()
|
|
58
71
|
CompositionLocalProvider(
|
|
59
|
-
AppTheme provides
|
|
60
|
-
|
|
61
|
-
AppStatusBar provides
|
|
72
|
+
AppTheme provides appTheme,
|
|
73
|
+
PlatformApi provides composeApi,
|
|
74
|
+
AppStatusBar provides statusBarHeight,
|
|
62
75
|
) {
|
|
63
76
|
content()
|
|
64
77
|
}
|
|
@@ -87,7 +87,9 @@ fun Screen(
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
val headerAnimated =
|
|
90
|
-
@Composable {
|
|
90
|
+
@Composable {
|
|
91
|
+
animatedHeader?.composable?.invoke(scrollState.value)
|
|
92
|
+
}
|
|
91
93
|
|
|
92
94
|
Box(
|
|
93
95
|
Modifier.fillMaxSize()
|
|
@@ -95,7 +97,10 @@ fun Screen(
|
|
|
95
97
|
) {
|
|
96
98
|
|
|
97
99
|
if (animatedHeader === null) {
|
|
98
|
-
HeaderBackground(
|
|
100
|
+
HeaderBackground(
|
|
101
|
+
headerType = headerType,
|
|
102
|
+
scrollState = scrollState.value,
|
|
103
|
+
)
|
|
99
104
|
} else {
|
|
100
105
|
Box(
|
|
101
106
|
Modifier.zIndex(2f)
|
|
@@ -104,7 +109,7 @@ fun Screen(
|
|
|
104
109
|
) {
|
|
105
110
|
HeaderBackground(
|
|
106
111
|
headerType = headerType,
|
|
107
|
-
scrollState = scrollState.value
|
|
112
|
+
scrollState = scrollState.value,
|
|
108
113
|
)
|
|
109
114
|
}
|
|
110
115
|
}
|
|
@@ -98,6 +98,7 @@ data class InputSearchProps(
|
|
|
98
98
|
val keyboardActions: KeyboardActions = KeyboardActions.Default,
|
|
99
99
|
val modifier: Modifier = Modifier,
|
|
100
100
|
val iconModifier: Modifier = Modifier,
|
|
101
|
+
val onClearPress: () -> Unit = {}
|
|
101
102
|
)
|
|
102
103
|
|
|
103
104
|
@Composable
|
|
@@ -176,8 +177,8 @@ fun InputSearch(
|
|
|
176
177
|
Text(
|
|
177
178
|
text = inputSearchProps.placeholder,
|
|
178
179
|
style = TextStyle(
|
|
179
|
-
fontSize =
|
|
180
|
-
lineHeight =
|
|
180
|
+
fontSize = 14.sp,
|
|
181
|
+
lineHeight = 20.sp,
|
|
181
182
|
fontWeight = inputSearchProps.fontWeight.value
|
|
182
183
|
),
|
|
183
184
|
maxLines = 1,
|
|
@@ -195,7 +196,10 @@ fun InputSearch(
|
|
|
195
196
|
size = 16.dp,
|
|
196
197
|
color = AppTheme.current.colors.text.hint,
|
|
197
198
|
modifier = Modifier.clickable(
|
|
198
|
-
onClick = {
|
|
199
|
+
onClick = {
|
|
200
|
+
inputSearchProps.text.value = ""
|
|
201
|
+
inputSearchProps.onClearPress.invoke()
|
|
202
|
+
},
|
|
199
203
|
interactionSource = remember { MutableInteractionSource() },
|
|
200
204
|
indication = null
|
|
201
205
|
)
|
|
@@ -211,20 +215,20 @@ fun InputSearch(
|
|
|
211
215
|
)
|
|
212
216
|
}
|
|
213
217
|
}
|
|
214
|
-
if (inputSearchProps.showButtonText) {
|
|
215
|
-
Text(
|
|
216
|
-
text = inputSearchProps.buttonText,
|
|
217
|
-
style = Typography.actionDefault,
|
|
218
|
-
color = AppTheme.current.colors.text.default,
|
|
219
|
-
modifier = Modifier.padding(start = Spacing.L).clickable(
|
|
220
|
-
interactionSource = remember { MutableInteractionSource() },
|
|
221
|
-
indication = null,
|
|
222
|
-
onClick = inputSearchProps.onPressButtonText
|
|
223
|
-
)
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
218
|
}
|
|
227
219
|
},
|
|
228
220
|
)
|
|
221
|
+
if (inputSearchProps.showButtonText) {
|
|
222
|
+
Text(
|
|
223
|
+
text = inputSearchProps.buttonText,
|
|
224
|
+
style = Typography.actionDefault,
|
|
225
|
+
color = AppTheme.current.colors.text.default,
|
|
226
|
+
modifier = Modifier.padding(start = Spacing.L).clickable(
|
|
227
|
+
interactionSource = remember { MutableInteractionSource() },
|
|
228
|
+
indication = null,
|
|
229
|
+
onClick = inputSearchProps.onPressButtonText
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
}
|
|
229
233
|
}
|
|
230
234
|
}
|
|
@@ -4,98 +4,35 @@ import SwiftUI
|
|
|
4
4
|
public struct Checkbox: View{
|
|
5
5
|
@Binding var checked: Bool
|
|
6
6
|
var disabled: Bool
|
|
7
|
-
|
|
8
|
-
var onCheckedChange: ((Bool) -> Void)?
|
|
9
|
-
var indeterminate: Bool
|
|
10
|
-
var title: String
|
|
7
|
+
var onChange: ((Bool)->Void)?
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
public init(_ checked: Binding<Bool>,disabled: Bool = false, onCheckedChange: ( (Bool) -> Void)?,
|
|
14
|
-
indeterminate: Bool = false,
|
|
15
|
-
title: String = ""){
|
|
9
|
+
public init(_ checked: Binding<Bool>,disabled: Bool = false, onChange: ((Bool)->Void)? = nil){
|
|
16
10
|
self._checked = checked
|
|
11
|
+
self.onChange = onChange
|
|
17
12
|
self.disabled = disabled
|
|
18
|
-
self.onCheckedChange = onCheckedChange
|
|
19
|
-
self.indeterminate = indeterminate
|
|
20
|
-
self.title = title
|
|
21
13
|
}
|
|
22
14
|
|
|
23
15
|
func onCheck(){
|
|
24
16
|
checked.toggle()
|
|
25
|
-
|
|
17
|
+
onChange?(checked)
|
|
26
18
|
}
|
|
27
19
|
|
|
28
|
-
public var body: some View
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.fill(backgroundColor)
|
|
34
|
-
.frame(width: 20, height: 20)
|
|
35
|
-
.overlay(
|
|
36
|
-
RoundedRectangle(cornerRadius: Radius.XS)
|
|
37
|
-
.stroke(borderColor, lineWidth: Spacing.XXS)
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
if checked {
|
|
41
|
-
Image(systemName: indeterminate ? "minus" : "checkmark")
|
|
20
|
+
public var body: some View{
|
|
21
|
+
return SwiftUI.Button(action: onCheck) {
|
|
22
|
+
HStack {
|
|
23
|
+
if (checked) {
|
|
24
|
+
Image(systemName: "checkmark.square.fill")
|
|
42
25
|
.resizable()
|
|
43
|
-
.foregroundColor(Colors.
|
|
44
|
-
.frame(width:
|
|
26
|
+
.foregroundColor(disabled ? Colors.black07 : Colors.primary)
|
|
27
|
+
.frame(width: 24, height: 24)
|
|
45
28
|
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Title
|
|
54
|
-
if !title.isEmpty {
|
|
55
|
-
Text(title)
|
|
56
|
-
.font(.description1)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private var borderColor: Color {
|
|
62
|
-
if disabled {
|
|
63
|
-
return Colors.black03
|
|
64
|
-
} else if checked {
|
|
65
|
-
return Colors.primary
|
|
66
|
-
} else {
|
|
67
|
-
return Colors.text
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private var backgroundColor: Color {
|
|
72
|
-
if disabled && checked {
|
|
73
|
-
return Colors.pink08
|
|
74
|
-
} else if checked {
|
|
75
|
-
return Colors.primary
|
|
76
|
-
} else {
|
|
77
|
-
return Colors.black01
|
|
29
|
+
}.frame(width: 24, height: 24)
|
|
30
|
+
.overlay(RoundedRectangle(cornerRadius: 4).stroke(Colors.black20,lineWidth: checked ? 0 : 1))
|
|
31
|
+
.background(Colors.black01)
|
|
32
|
+
|
|
78
33
|
}
|
|
34
|
+
.opacity(disabled ? 0.4 : 1)
|
|
35
|
+
.disabled(disabled)
|
|
79
36
|
}
|
|
80
37
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// public var body: some View{
|
|
84
|
-
// return SwiftUI.Button(action: onCheck) {
|
|
85
|
-
// HStack {
|
|
86
|
-
// if (checked) {
|
|
87
|
-
// Image(systemName: "checkmark.square.fill")
|
|
88
|
-
// .resizable()
|
|
89
|
-
// .foregroundColor(disabled ? Colors.black07 : Colors.primary)
|
|
90
|
-
// .frame(width: 24, height: 24)
|
|
91
|
-
// }
|
|
92
|
-
// }.frame(width: 24, height: 24)
|
|
93
|
-
// .overlay(RoundedRectangle(cornerRadius: 4).stroke(Colors.black20,lineWidth: checked ? 0 : 1))
|
|
94
|
-
// .background(Colors.black01)
|
|
95
|
-
//
|
|
96
|
-
// }
|
|
97
|
-
// .opacity(disabled ? 0.4 : 1)
|
|
98
|
-
// .disabled(disabled)
|
|
99
|
-
// }
|
|
100
|
-
|
|
101
38
|
}
|
package/ios/Input/Input.swift
CHANGED
|
@@ -71,22 +71,12 @@ public struct Input: View {
|
|
|
71
71
|
var rightIcon: AnyView?
|
|
72
72
|
var onChange: ((String) -> Void)?
|
|
73
73
|
var onBlur: (() -> Void)?
|
|
74
|
-
var floatingIconURL: String?
|
|
75
|
-
var floatingIconColor: Color
|
|
76
|
-
var size: InputSize
|
|
77
|
-
var fontWeight: InputFontWeight
|
|
78
|
-
var disabled: Bool
|
|
79
|
-
var leadingIcon: String?
|
|
80
|
-
var leadingIconColor: Color
|
|
81
|
-
var loading: Bool
|
|
82
|
-
var required: Bool
|
|
83
|
-
var hintText: String?
|
|
84
74
|
|
|
85
75
|
func onCancel(){
|
|
86
76
|
self.value.removeAll()
|
|
87
77
|
}
|
|
88
78
|
|
|
89
|
-
public init(_ value: Binding<String>, placeholder: String = "", floatingValue: String = "", multilines: Bool = false, errorMessage: String = "", isSecure: Bool = false, keyboardType: UIKeyboardType = .default, floatingIcon: AnyView? = nil, rightIcon: AnyView? = nil, onChange: ((String)->Void)? = nil, onBlur: (()->Void)? = nil
|
|
79
|
+
public init(_ value: Binding<String>, placeholder: String = "", floatingValue: String = "", multilines: Bool = false, errorMessage: String = "", isSecure: Bool = false, keyboardType: UIKeyboardType = .default, floatingIcon: AnyView? = nil, rightIcon: AnyView? = nil, onChange: ((String)->Void)? = nil, onBlur: (()->Void)? = nil){
|
|
90
80
|
self.placeholder = placeholder
|
|
91
81
|
self.floatingValue = floatingValue
|
|
92
82
|
self.errorMessage = errorMessage
|
|
@@ -96,16 +86,6 @@ public struct Input: View {
|
|
|
96
86
|
self.rightIcon = rightIcon
|
|
97
87
|
self.floatingIcon = floatingIcon
|
|
98
88
|
self.onChange = onChange
|
|
99
|
-
self.floatingIconURL = floatingIconURL
|
|
100
|
-
self.floatingIconColor = floatingIconColor
|
|
101
|
-
self.size = size
|
|
102
|
-
self.fontWeight = fontWeight
|
|
103
|
-
self.disabled = disabled
|
|
104
|
-
self.leadingIcon = leadingIcon
|
|
105
|
-
self.leadingIconColor = leadingIconColor
|
|
106
|
-
self.loading = loading
|
|
107
|
-
self.required = required
|
|
108
|
-
self.hintText = hintText
|
|
109
89
|
}
|
|
110
90
|
|
|
111
91
|
func onChangeText(value: String){
|
|
@@ -113,137 +93,57 @@ public struct Input: View {
|
|
|
113
93
|
}
|
|
114
94
|
|
|
115
95
|
public var body: some View {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.
|
|
96
|
+
return ZStack (alignment: .topLeading){
|
|
97
|
+
HStack(alignment: .center){
|
|
98
|
+
if (!isSecure){
|
|
99
|
+
TextField("",text: $value, onEditingChanged:{ isChange in
|
|
100
|
+
self.focused = isChange
|
|
101
|
+
})
|
|
102
|
+
.font(.paragraph)
|
|
103
|
+
.padding(.horizontal, 12)
|
|
104
|
+
.accentColor(Colors.pink03)
|
|
105
|
+
.keyboardType(keyboardType)
|
|
106
|
+
.valueChanged(value: value, onChange: onChangeText)
|
|
107
|
+
|
|
108
|
+
HStack{
|
|
109
|
+
SwiftButton(action: onCancel){
|
|
110
|
+
Image(systemName: "x.circle.fill")
|
|
111
|
+
.foregroundColor(Colors.text)
|
|
112
|
+
.frame(width: 16, height: 16)
|
|
113
|
+
.padding(.leading, -4)
|
|
114
|
+
.padding(.trailing, 8)
|
|
134
115
|
}
|
|
135
116
|
|
|
136
|
-
if loading {
|
|
137
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
138
|
-
.frame(width: 20, height: 20)
|
|
139
|
-
} else if !value.isEmpty {
|
|
140
|
-
SwiftButton(action: onCancel){
|
|
141
|
-
Image(systemName: "x.circle.fill")
|
|
142
|
-
.foregroundColor(Colors.text)
|
|
143
|
-
.frame(width: 16, height: 16)
|
|
144
|
-
.padding(.leading, -4)
|
|
145
|
-
.padding(.trailing, 8)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
117
|
rightIcon
|
|
118
|
+
|
|
150
119
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.
|
|
159
|
-
.padding(.
|
|
160
|
-
.padding(.
|
|
161
|
-
.allowsHitTesting(false)
|
|
120
|
+
}else{
|
|
121
|
+
DotTextField(value: $value)
|
|
122
|
+
.padding(.horizontal, 12)
|
|
123
|
+
.accentColor(Colors.pink03)
|
|
124
|
+
SwiftButton(action: onCancel){
|
|
125
|
+
Image(systemName: "x.circle.fill")
|
|
126
|
+
.foregroundColor(Colors.text)
|
|
127
|
+
.frame(width: 16, height: 16)
|
|
128
|
+
.padding(.leading, -4)
|
|
129
|
+
.padding(.trailing, 8)
|
|
162
130
|
}
|
|
163
|
-
|
|
164
|
-
if !floatingValue.isEmpty {
|
|
165
|
-
FloatingComponent(floatingValue, floatingIcon: floatingIconURL.map { url in
|
|
166
|
-
AnyView(ImageLoader(url: URL(string: url)!)
|
|
167
|
-
.frame(width: 20, height: 20)
|
|
168
|
-
.background(floatingIconColor))
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if !errorMessage.isEmpty {
|
|
174
|
-
Text(errorMessage)
|
|
175
|
-
.foregroundColor(.red)
|
|
176
|
-
.font(.caption)
|
|
177
|
-
} else if let hint = hintText {
|
|
178
|
-
Text(hint)
|
|
179
|
-
.foregroundColor(Colors.black09)
|
|
180
|
-
.font(.caption)
|
|
181
131
|
}
|
|
182
132
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
Group {
|
|
195
|
-
if let image = loader.image {
|
|
196
|
-
Image(uiImage: image)
|
|
197
|
-
.resizable()
|
|
198
|
-
.aspectRatio(contentMode: .fit)
|
|
199
|
-
} else {
|
|
200
|
-
// Use UIActivityIndicatorView for earlier iOS versions
|
|
201
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
133
|
+
.frame(maxWidth: .infinity, minHeight: 48)
|
|
134
|
+
.overlay(RoundedRectangle(cornerRadius: 8).stroke(focused == true ? Colors.pink03 : Colors.border, lineWidth: 1))
|
|
135
|
+
.padding(.horizontal, 1)
|
|
136
|
+
if (value.isEmpty){
|
|
137
|
+
Text(placeholder)
|
|
138
|
+
.font(.paragraph)
|
|
139
|
+
.font(.system(size: 16))
|
|
140
|
+
.foregroundColor(Colors.black09)
|
|
141
|
+
.padding(.leading, 12)
|
|
142
|
+
.padding(.top, 16)
|
|
143
|
+
.allowsHitTesting(false)
|
|
202
144
|
}
|
|
203
|
-
|
|
145
|
+
if (!floatingValue.isEmpty) {FloatingComponent(floatingValue, floatingIcon: floatingIcon)}
|
|
146
|
+
}.padding(.bottom, 8)
|
|
204
147
|
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
struct ActivityIndicator: UIViewRepresentable {
|
|
209
|
-
@Binding var isAnimating: Bool
|
|
210
|
-
let style: UIActivityIndicatorView.Style
|
|
211
|
-
|
|
212
|
-
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
|
|
213
|
-
return UIActivityIndicatorView(style: style)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
|
|
217
|
-
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
class ImageLoaderObject: ObservableObject {
|
|
222
|
-
@Published var image: UIImage?
|
|
223
|
-
private let url: URL
|
|
224
148
|
|
|
225
|
-
init(url: URL) {
|
|
226
|
-
self.url = url
|
|
227
|
-
loadImage()
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
private func loadImage() {
|
|
231
|
-
URLSession.shared.dataTask(with: url) { data, _, _ in
|
|
232
|
-
if let data = data, let loadedImage = UIImage(data: data) {
|
|
233
|
-
DispatchQueue.main.async {
|
|
234
|
-
self.image = loadedImage
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}.resume()
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
public enum InputSize {
|
|
242
|
-
case small
|
|
243
|
-
case large
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
public enum InputFontWeight {
|
|
247
|
-
case regular
|
|
248
|
-
case bold
|
|
249
149
|
}
|
package/local.properties
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
# Location of the SDK. This is only used by Gradle.
|
|
5
5
|
# For customization when using a Version Control System, please read the
|
|
6
6
|
# header note.
|
|
7
|
-
#
|
|
8
|
-
sdk.dir=/Users/
|
|
7
|
+
#Tue Apr 23 16:40:15 ICT 2024
|
|
8
|
+
sdk.dir=/Users/hung.dao1/Library/Android/sdk
|
package/package.json
CHANGED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// InputSearch.swift
|
|
3
|
-
// Pods
|
|
4
|
-
//
|
|
5
|
-
// Created by dung.pham2 on 3/12/24.
|
|
6
|
-
//
|
|
7
|
-
import SwiftUI
|
|
8
|
-
|
|
9
|
-
// MARK: - RenderRightIconSearch
|
|
10
|
-
|
|
11
|
-
struct RenderRightIconSearch: View {
|
|
12
|
-
let loading: Bool
|
|
13
|
-
let icon: String
|
|
14
|
-
let color: Color
|
|
15
|
-
let onClick: () -> Void
|
|
16
|
-
|
|
17
|
-
var body: some View {
|
|
18
|
-
HStack(spacing: 0) {
|
|
19
|
-
if loading {
|
|
20
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
21
|
-
.frame(width: 16, height: 16)
|
|
22
|
-
.padding(.horizontal, Spacing.S)
|
|
23
|
-
}
|
|
24
|
-
if !icon.isEmpty {
|
|
25
|
-
Divider()
|
|
26
|
-
.frame(width: 1)
|
|
27
|
-
.background(Colors.primary)
|
|
28
|
-
.padding(.leading, Spacing.S)
|
|
29
|
-
|
|
30
|
-
Image(systemName: icon)
|
|
31
|
-
.foregroundColor(color)
|
|
32
|
-
.frame(width: 24, height: 24)
|
|
33
|
-
.padding(.leading, Spacing.S)
|
|
34
|
-
.onTapGesture(perform: onClick)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// MARK: - InputSearch
|
|
41
|
-
|
|
42
|
-
public struct InputSearch: View {
|
|
43
|
-
@Binding var text: String
|
|
44
|
-
var buttonText: String = ""
|
|
45
|
-
var showButtonText: Bool = false
|
|
46
|
-
var showBorder: Bool = true
|
|
47
|
-
var placeholder: String = ""
|
|
48
|
-
var onChangeText: (String) -> Void = { _ in }
|
|
49
|
-
var onPressButtonText: () -> Void = {}
|
|
50
|
-
var error: String = ""
|
|
51
|
-
var disabled: Bool = false
|
|
52
|
-
var icon: String = ""
|
|
53
|
-
var iconColor: Color = Colors.text
|
|
54
|
-
var onRightIconPressed: () -> Void = {}
|
|
55
|
-
var onFocus: () -> Void = {}
|
|
56
|
-
var onBlur: () -> Void = {}
|
|
57
|
-
var loading: Bool = false
|
|
58
|
-
var fontWeight: Font.Weight = .regular
|
|
59
|
-
var keyboardType: UIKeyboardType = .default
|
|
60
|
-
|
|
61
|
-
@State private var isFocused = false
|
|
62
|
-
@State private var isBlurred = false
|
|
63
|
-
|
|
64
|
-
public init(text: Binding<String>,
|
|
65
|
-
buttonText: String = "",
|
|
66
|
-
showButtonText: Bool = false,
|
|
67
|
-
showBorder: Bool = true,
|
|
68
|
-
placeholder: String = "",
|
|
69
|
-
onChangeText: @escaping (String) -> Void = { _ in },
|
|
70
|
-
onPressButtonText: @escaping () -> Void = {},
|
|
71
|
-
error: String = "",
|
|
72
|
-
disabled: Bool = false,
|
|
73
|
-
icon: String = "",
|
|
74
|
-
iconColor: Color = Colors.text,
|
|
75
|
-
onRightIconPressed: @escaping () -> Void = {},
|
|
76
|
-
onFocus: @escaping () -> Void = {},
|
|
77
|
-
onBlur: @escaping () -> Void = {},
|
|
78
|
-
loading: Bool = false,
|
|
79
|
-
fontWeight: Font.Weight = .regular,
|
|
80
|
-
keyboardType: UIKeyboardType = .default) {
|
|
81
|
-
self._text = text
|
|
82
|
-
self.buttonText = buttonText
|
|
83
|
-
self.showButtonText = showButtonText
|
|
84
|
-
self.showBorder = showBorder
|
|
85
|
-
self.placeholder = placeholder
|
|
86
|
-
self.onChangeText = onChangeText
|
|
87
|
-
self.onPressButtonText = onPressButtonText
|
|
88
|
-
self.error = error
|
|
89
|
-
self.disabled = disabled
|
|
90
|
-
self.icon = icon
|
|
91
|
-
self.iconColor = iconColor
|
|
92
|
-
self.onRightIconPressed = onRightIconPressed
|
|
93
|
-
self.onFocus = onFocus
|
|
94
|
-
self.onBlur = onBlur
|
|
95
|
-
self.loading = loading
|
|
96
|
-
self.fontWeight = fontWeight
|
|
97
|
-
self.keyboardType = keyboardType
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
public var body: some View {
|
|
102
|
-
VStack(alignment: .leading, spacing: 0) {
|
|
103
|
-
HStack(spacing: 0) {
|
|
104
|
-
HStack {
|
|
105
|
-
Image(systemName: "magnifyingglass")
|
|
106
|
-
.foregroundColor(Colors.black12)
|
|
107
|
-
.frame(width: 24, height: 24)
|
|
108
|
-
.padding(.trailing, Spacing.XS)
|
|
109
|
-
|
|
110
|
-
ZStack(alignment: .leading) {
|
|
111
|
-
if text.isEmpty {
|
|
112
|
-
Text(placeholder)
|
|
113
|
-
.foregroundColor(disabled ? Colors.black08 : Colors.black12)
|
|
114
|
-
.font(.system(size: 16, weight: fontWeight))
|
|
115
|
-
}
|
|
116
|
-
TextField("", text: $text, onEditingChanged: { editing in
|
|
117
|
-
isFocused = editing
|
|
118
|
-
if editing {
|
|
119
|
-
onFocus()
|
|
120
|
-
} else if isBlurred {
|
|
121
|
-
onBlur()
|
|
122
|
-
}
|
|
123
|
-
if editing && !isBlurred {
|
|
124
|
-
isBlurred = true
|
|
125
|
-
}
|
|
126
|
-
}, onCommit: {})
|
|
127
|
-
.disabled(disabled)
|
|
128
|
-
.foregroundColor(disabled ? Colors.black08 : Colors.black17)
|
|
129
|
-
.font(.system(size: 15, weight: fontWeight))
|
|
130
|
-
.keyboardType(keyboardType)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if isFocused && !text.isEmpty {
|
|
134
|
-
SwiftButton(action: { text = "" }) {
|
|
135
|
-
Image(systemName: "xmark.circle.fill")
|
|
136
|
-
.foregroundColor(Colors.black12)
|
|
137
|
-
.frame(width: 16, height: 16)
|
|
138
|
-
}
|
|
139
|
-
.padding(.leading, Spacing.S)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
RenderRightIconSearch(loading: loading, icon: icon, color: disabled ? Colors.black08 : iconColor, onClick: onRightIconPressed)
|
|
143
|
-
}
|
|
144
|
-
.padding(.horizontal, Spacing.M)
|
|
145
|
-
.padding(.vertical, Spacing.S)
|
|
146
|
-
.background(Colors.black01)
|
|
147
|
-
.cornerRadius(Radius.XL)
|
|
148
|
-
.overlay(
|
|
149
|
-
RoundedRectangle(cornerRadius: Radius.XL)
|
|
150
|
-
.stroke(getBorderColor(isFocused: isFocused, error: error, disabled: disabled), lineWidth: showBorder ? 1 : 0)
|
|
151
|
-
)
|
|
152
|
-
.frame(maxWidth: showButtonText ? .infinity : nil, alignment: .leading)
|
|
153
|
-
|
|
154
|
-
if showButtonText {
|
|
155
|
-
SwiftButton(action: onPressButtonText) {
|
|
156
|
-
Text(buttonText)
|
|
157
|
-
.foregroundColor(Colors.black17)
|
|
158
|
-
.font(.system(size: 14, weight: .medium))
|
|
159
|
-
}
|
|
160
|
-
.padding(.leading, Spacing.L)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// MARK: - Helper Functions
|
|
168
|
-
|
|
169
|
-
func getBorderColor(isFocused: Bool, error: String, disabled: Bool) -> Color {
|
|
170
|
-
if disabled {
|
|
171
|
-
return Colors.black08
|
|
172
|
-
} else if !error.isEmpty {
|
|
173
|
-
return Colors.red03
|
|
174
|
-
} else if isFocused {
|
|
175
|
-
return Colors.primary
|
|
176
|
-
} else {
|
|
177
|
-
return Colors.black04
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// InputTextArea.swift
|
|
3
|
-
// Pods
|
|
4
|
-
//
|
|
5
|
-
// Created by dung.pham2 on 2/12/24.
|
|
6
|
-
//
|
|
7
|
-
import SwiftUI
|
|
8
|
-
|
|
9
|
-
// Make these constants public
|
|
10
|
-
public let MAX_LENGTH = 300
|
|
11
|
-
public let DEFAULT_HEIGHT: CGFloat = 104
|
|
12
|
-
|
|
13
|
-
struct UITextViewWrapper: UIViewRepresentable {
|
|
14
|
-
@Binding var text: String
|
|
15
|
-
var onDone: (() -> Void)?
|
|
16
|
-
var onFocus: (() -> Void)?
|
|
17
|
-
var onBlur: (() -> Void)?
|
|
18
|
-
|
|
19
|
-
func makeUIView(context: Context) -> UITextView {
|
|
20
|
-
let textView = UITextView()
|
|
21
|
-
textView.delegate = context.coordinator
|
|
22
|
-
return textView
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
func updateUIView(_ uiView: UITextView, context: Context) {
|
|
26
|
-
uiView.text = text
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
func makeCoordinator() -> Coordinator {
|
|
30
|
-
Coordinator(self)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
class Coordinator: NSObject, UITextViewDelegate {
|
|
34
|
-
var parent: UITextViewWrapper
|
|
35
|
-
|
|
36
|
-
init(_ parent: UITextViewWrapper) {
|
|
37
|
-
self.parent = parent
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func textViewDidChange(_ textView: UITextView) {
|
|
41
|
-
parent.text = textView.text
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
func textViewDidBeginEditing(_ textView: UITextView) {
|
|
45
|
-
parent.onFocus?()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
func textViewDidEndEditing(_ textView: UITextView) {
|
|
49
|
-
parent.onBlur?()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
|
53
|
-
if text == "\n" {
|
|
54
|
-
parent.onDone?()
|
|
55
|
-
return false
|
|
56
|
-
}
|
|
57
|
-
return true
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Make the struct public
|
|
63
|
-
public struct InputTextArea: View {
|
|
64
|
-
@Binding var text: String
|
|
65
|
-
var maxLength: Int = MAX_LENGTH
|
|
66
|
-
var height: CGFloat = DEFAULT_HEIGHT
|
|
67
|
-
var floatingValue: String = ""
|
|
68
|
-
var floatingValueColor: Color = .gray
|
|
69
|
-
var floatingIcon: String = ""
|
|
70
|
-
var floatingIconColor: Color = .black
|
|
71
|
-
var placeholder: String = ""
|
|
72
|
-
var onChangeText: (String) -> Void = { _ in }
|
|
73
|
-
var error: String = ""
|
|
74
|
-
var errorSpacing: Bool = false
|
|
75
|
-
var disabled: Bool = false
|
|
76
|
-
var icon: String = ""
|
|
77
|
-
var iconColor: Color = .black
|
|
78
|
-
var onRightIconPressed: () -> Void = {}
|
|
79
|
-
var onFocus: () -> Void = {}
|
|
80
|
-
var onBlur: () -> Void = {}
|
|
81
|
-
var loading: Bool = false
|
|
82
|
-
var required: Bool = false
|
|
83
|
-
var fontWeight: Font.Weight = .regular
|
|
84
|
-
var keyboardType: UIKeyboardType = .default
|
|
85
|
-
|
|
86
|
-
@State private var isFocused = false
|
|
87
|
-
@State private var isBlurred = false
|
|
88
|
-
|
|
89
|
-
// Make the initializer public
|
|
90
|
-
public init(
|
|
91
|
-
text: Binding<String>,
|
|
92
|
-
maxLength: Int = MAX_LENGTH,
|
|
93
|
-
height: CGFloat = DEFAULT_HEIGHT,
|
|
94
|
-
floatingValue: String = "",
|
|
95
|
-
floatingValueColor: Color = .gray,
|
|
96
|
-
floatingIcon: String = "",
|
|
97
|
-
floatingIconColor: Color = .black,
|
|
98
|
-
placeholder: String = "",
|
|
99
|
-
onChangeText: @escaping (String) -> Void = { _ in },
|
|
100
|
-
error: String = "",
|
|
101
|
-
errorSpacing: Bool = false,
|
|
102
|
-
disabled: Bool = false,
|
|
103
|
-
icon: String = "",
|
|
104
|
-
iconColor: Color = Colors.pink03,
|
|
105
|
-
onRightIconPressed: @escaping () -> Void = {},
|
|
106
|
-
onFocus: @escaping () -> Void = {},
|
|
107
|
-
onBlur: @escaping () -> Void = {},
|
|
108
|
-
loading: Bool = false,
|
|
109
|
-
required: Bool = false,
|
|
110
|
-
fontWeight: Font.Weight = .regular,
|
|
111
|
-
keyboardType: UIKeyboardType = .default
|
|
112
|
-
) {
|
|
113
|
-
self._text = text
|
|
114
|
-
self.maxLength = maxLength
|
|
115
|
-
self.height = height
|
|
116
|
-
self.floatingValue = floatingValue
|
|
117
|
-
self.floatingValueColor = floatingValueColor
|
|
118
|
-
self.floatingIcon = floatingIcon
|
|
119
|
-
self.floatingIconColor = floatingIconColor
|
|
120
|
-
self.placeholder = placeholder
|
|
121
|
-
self.onChangeText = onChangeText
|
|
122
|
-
self.error = error
|
|
123
|
-
self.errorSpacing = errorSpacing
|
|
124
|
-
self.disabled = disabled
|
|
125
|
-
self.icon = icon
|
|
126
|
-
self.iconColor = iconColor
|
|
127
|
-
self.onRightIconPressed = onRightIconPressed
|
|
128
|
-
self.onFocus = onFocus
|
|
129
|
-
self.onBlur = onBlur
|
|
130
|
-
self.loading = loading
|
|
131
|
-
self.required = required
|
|
132
|
-
self.fontWeight = fontWeight
|
|
133
|
-
self.keyboardType = keyboardType
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public var body: some View {
|
|
137
|
-
VStack(alignment: .leading, spacing: 0) {
|
|
138
|
-
ZStack(alignment: .topLeading) {
|
|
139
|
-
if !floatingValue.isEmpty || !floatingIcon.isEmpty {
|
|
140
|
-
HStack(spacing: 4) {
|
|
141
|
-
Text(floatingValue)
|
|
142
|
-
.font(.caption)
|
|
143
|
-
.foregroundColor(disabled ? .gray : floatingValueColor)
|
|
144
|
-
|
|
145
|
-
if required {
|
|
146
|
-
Text("*")
|
|
147
|
-
.font(.caption)
|
|
148
|
-
.foregroundColor(.red)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if !floatingIcon.isEmpty {
|
|
152
|
-
Image(systemName: floatingIcon)
|
|
153
|
-
.font(.caption)
|
|
154
|
-
.foregroundColor(disabled ? .gray : floatingIconColor)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
.padding(.horizontal, 8)
|
|
158
|
-
.background(Colors.black01)
|
|
159
|
-
.offset(x: Spacing.S, y: -height / 10)
|
|
160
|
-
.zIndex(10)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
VStack {
|
|
164
|
-
ZStack(alignment: .topLeading) {
|
|
165
|
-
if text.isEmpty {
|
|
166
|
-
Text(placeholder)
|
|
167
|
-
.foregroundColor(disabled ? .gray : .gray)
|
|
168
|
-
.font(.system(size: 16, weight: fontWeight))
|
|
169
|
-
.padding(.horizontal, 16)
|
|
170
|
-
.padding(.vertical, 12)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
UITextViewWrapper(text: $text, onDone: {
|
|
174
|
-
// Handle done action if needed
|
|
175
|
-
},
|
|
176
|
-
onFocus: {
|
|
177
|
-
isFocused = true
|
|
178
|
-
onFocus()
|
|
179
|
-
}, onBlur: {
|
|
180
|
-
isFocused = false
|
|
181
|
-
isBlurred = true
|
|
182
|
-
onBlur()
|
|
183
|
-
})
|
|
184
|
-
.frame(height: height)
|
|
185
|
-
.padding(.horizontal, 12)
|
|
186
|
-
.padding(.vertical, 8)
|
|
187
|
-
.disabled(disabled) }
|
|
188
|
-
|
|
189
|
-
HStack {
|
|
190
|
-
Spacer()
|
|
191
|
-
Text("\(text.count)/\(maxLength)")
|
|
192
|
-
.font(.caption)
|
|
193
|
-
.foregroundColor(.gray)
|
|
194
|
-
}
|
|
195
|
-
.padding(.horizontal, 16)
|
|
196
|
-
.padding(.bottom, 12)
|
|
197
|
-
}
|
|
198
|
-
.background(Color.white)
|
|
199
|
-
.cornerRadius(8)
|
|
200
|
-
.overlay(
|
|
201
|
-
RoundedRectangle(cornerRadius: 8)
|
|
202
|
-
.stroke(borderColor, lineWidth: 1)
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if !error.isEmpty {
|
|
207
|
-
Text(error)
|
|
208
|
-
.font(.caption)
|
|
209
|
-
.foregroundColor(.red)
|
|
210
|
-
.padding(.top, errorSpacing ? 8 : 4)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in
|
|
214
|
-
if !isBlurred {
|
|
215
|
-
isFocused = true
|
|
216
|
-
onFocus()
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
|
|
220
|
-
if isFocused {
|
|
221
|
-
isFocused = false
|
|
222
|
-
isBlurred = true
|
|
223
|
-
onBlur()
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
private var borderColor: Color {
|
|
229
|
-
if disabled {
|
|
230
|
-
return Colors.black03
|
|
231
|
-
} else if !error.isEmpty {
|
|
232
|
-
return .red
|
|
233
|
-
} else if isFocused {
|
|
234
|
-
return Colors.pink03
|
|
235
|
-
} else {
|
|
236
|
-
return Colors.border
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|