@momo-kits/native-kits 0.155.1-test.2 → 0.155.1-test.20

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.
@@ -1,188 +1,285 @@
1
1
  import Foundation
2
2
  import SwiftUI
3
3
 
4
-
5
- fileprivate struct FloatingComponent: View {
6
- var floatingValue: String
7
- var floatingIcon: AnyView?
8
-
9
- public init (_ floatingValue: String = "", floatingIcon: AnyView? = nil){
10
- self.floatingValue = floatingValue
11
- self.floatingIcon = floatingIcon
12
- }
13
-
14
- public var body: some View {
15
- return HStack {
16
- Text(floatingValue)
17
- .font(.paragraph)
18
- .foregroundColor(Colors.black09)
19
- .font(.system(size: 12))
20
- .padding(.leading, 8)
21
- .padding(.trailing, 8)
22
- floatingIcon
23
- }
24
- .background(Colors.black01)
25
- .offset(x: 16, y:-8)
26
- .zIndex(10)
27
- }
28
- }
29
-
30
- struct DotTextField: View{
31
- @Binding var value: String
32
- @State var isHide: Bool = true
33
-
34
-
35
- func onToggle(){
36
- isHide.toggle()
37
- }
38
-
39
- public var body: some View {
40
- return HStack {
41
- if (isHide){
42
- SecureField("", text: $value)
43
- }
44
- else {
45
- TextField("", text: $value)
46
- }
47
-
48
- SwiftButton(action: onToggle){
49
- Image(systemName: isHide ? "eye.slash" : "eye")
50
- .foregroundColor(Colors.text)
51
- .frame(width: 16, height: 16)
52
- .padding(.leading, -4)
53
- }
54
-
55
-
56
- }
57
-
58
- }
59
- }
60
-
61
4
  public struct Input: View {
62
- @Binding var value: String
63
- @State var focused: Bool = false
5
+ @Binding public var text: String
64
6
 
65
- var placeholder: String
66
- var floatingValue: String
67
- var errorMessage: String
68
- var errorSpacing: Bool
69
- var isSecure: Bool
70
- var keyboardType: UIKeyboardType
71
- var floatingIcon: AnyView?
72
- var rightIcon: AnyView?
73
- var onChange: ((String) -> Void)?
74
- var onBlur: (() -> Void)?
75
- var floatingIconURL: String?
76
- var floatingIconColor: Color?
77
- var size: InputSize
78
- var fontWeight: InputFontWeight
79
- var disabled: Bool
80
- var leadingIcon: String?
81
- var leadingIconColor: Color?
82
- var loading: Bool
83
- var required: Bool
84
- var hintText: String?
7
+ public var placeholder: String
8
+ public var floatingValue: String
9
+ public var floatingIcon: String
10
+ public var floatingIconColor: Color
11
+ public var size: InputSize
12
+ public var hintText: String
13
+ public var error: String
14
+ public var errorSpacing: Bool
15
+ public var disabled: Bool
16
+ public var readOnly: Bool
17
+ public var secureTextEntry: Bool
18
+ public var rightIcon: String
19
+ public var rightIconColor: Color
20
+ public var leadingIcon: String
21
+ public var leadingIconColor: Color
22
+ public var loading: Bool
23
+ public var required: Bool
24
+ public var fontWeight: InputFontWeight
25
+ public var keyboardType: UIKeyboardType
26
+ public var autofocus: Bool
27
+ public var onChangeText: ((String) -> Void)?
28
+ public var onFocus: (() -> Void)?
29
+ public var onBlur: (() -> Void)?
30
+ public var onRightIconPressed: (() -> Void)?
85
31
 
86
- func onCancel(){
87
- self.value.removeAll()
88
- }
32
+ @State private var isFocused: Bool = false
33
+ @State private var isPasswordHidden: Bool = true
34
+ @FocusState private var isTextFieldFocused: Bool
89
35
 
90
- 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, floatingIconURL: String? = nil, floatingIconColor: Color? = Colors.pink03, size: InputSize = .small, fontWeight: InputFontWeight = .regular, disabled: Bool = false, leadingIcon: String? = nil, leadingIconColor: Color? = Colors.pink03, loading: Bool = false, required: Bool = false, hintText: String? = nil, errorSpacing: Bool = false){
36
+ public init(
37
+ text: Binding<String>,
38
+ placeholder: String = "",
39
+ floatingValue: String = "",
40
+ floatingIcon: String = "",
41
+ floatingIconColor: Color = Colors.primary,
42
+ size: InputSize = .small,
43
+ hintText: String = "",
44
+ error: String = "",
45
+ errorSpacing: Bool = false,
46
+ disabled: Bool = false,
47
+ readOnly: Bool = false,
48
+ secureTextEntry: Bool = false,
49
+ rightIcon: String = "",
50
+ rightIconColor: Color = Colors.black17,
51
+ leadingIcon: String = "",
52
+ leadingIconColor: Color = Colors.black12,
53
+ loading: Bool = false,
54
+ required: Bool = false,
55
+ fontWeight: InputFontWeight = .regular,
56
+ keyboardType: UIKeyboardType = .default,
57
+ autofocus: Bool = false,
58
+ onChangeText: ((String) -> Void)? = nil,
59
+ onFocus: (() -> Void)? = nil,
60
+ onBlur: (() -> Void)? = nil,
61
+ onRightIconPressed: (() -> Void)? = nil
62
+ ) {
63
+ self._text = text
91
64
  self.placeholder = placeholder
92
65
  self.floatingValue = floatingValue
93
- self.errorMessage = errorMessage
94
- self.isSecure = isSecure
95
- self._value = value
96
- self.keyboardType = keyboardType
97
- self.rightIcon = rightIcon
98
66
  self.floatingIcon = floatingIcon
99
- self.onChange = onChange
100
- self.floatingIconURL = floatingIconURL
101
67
  self.floatingIconColor = floatingIconColor
102
68
  self.size = size
103
- self.fontWeight = fontWeight
69
+ self.hintText = hintText
70
+ self.error = error
71
+ self.errorSpacing = errorSpacing
104
72
  self.disabled = disabled
73
+ self.readOnly = readOnly
74
+ self.secureTextEntry = secureTextEntry
75
+ self.rightIcon = rightIcon
76
+ self.rightIconColor = rightIconColor
105
77
  self.leadingIcon = leadingIcon
106
78
  self.leadingIconColor = leadingIconColor
107
79
  self.loading = loading
108
80
  self.required = required
109
- self.hintText = hintText
110
- self.errorSpacing = errorSpacing
111
- }
112
-
113
- func onChangeText(value: String){
114
- onChange?(value)
81
+ self.fontWeight = fontWeight
82
+ self.keyboardType = keyboardType
83
+ self.autofocus = autofocus
84
+ self.onChangeText = onChangeText
85
+ self.onFocus = onFocus
86
+ self.onBlur = onBlur
87
+ self.onRightIconPressed = onRightIconPressed
115
88
  }
116
-
89
+
90
+ // MARK: - Body
117
91
  public var body: some View {
118
- VStack(alignment: .leading, spacing: 4) {
119
- ZStack(alignment: .topLeading) {
120
- HStack(alignment: .center) {
121
- if let leadingIcon = leadingIcon {
122
- Icon(source: leadingIcon, size: 16, color: leadingIconColor)
92
+ VStack(alignment: .leading, spacing: 4) {
93
+ ZStack(alignment: .topLeading) {
94
+ // Floating label
95
+ if !floatingValue.isEmpty || !floatingIcon.isEmpty {
96
+ HStack(spacing: Spacing.XS) {
97
+ MomoText(floatingValue, typography: .labelSMedium, color: getFloatingColor())
98
+ if required {
99
+ MomoText("*", typography: .labelSMedium, color: Colors.red03)
123
100
  }
124
-
125
- if !isSecure {
126
- TextField("", text: $value, onEditingChanged: { isChange in
127
- self.focused = isChange
128
- })
129
- .disabled(disabled)
130
- .font(fontWeight == .bold ? .headline : .body)
131
- } else {
132
- DotTextField(value: $value)
133
- .disabled(disabled)
101
+ if !floatingIcon.isEmpty {
102
+ Icon(source: floatingIcon, size: 16, color: getFloatingIconColor())
103
+ }
104
+ }
105
+ .padding(.horizontal, Spacing.S)
106
+ .background(Colors.black01)
107
+ .offset(x: Spacing.S, y: -8)
108
+ .zIndex(10)
109
+ }
110
+
111
+ // Input container
112
+ HStack(alignment: .center, spacing: 0) {
113
+ // Leading icon
114
+ if !leadingIcon.isEmpty {
115
+ Icon(source: leadingIcon, size: size == .small ? 24 : 32, color: leadingIconColor)
116
+ .padding(.trailing, Spacing.M)
117
+ }
118
+
119
+ // Text input field
120
+ ZStack(alignment: .leading) {
121
+ if text.isEmpty {
122
+ MomoText(
123
+ placeholder + (required && floatingValue.isEmpty ? " *" : ""),
124
+ typography: fontWeight == .bold ? .actionSBold : .bodyDefaultRegular,
125
+ color: getPlaceholderColor()
126
+ )
134
127
  }
135
128
 
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)
129
+ if secureTextEntry && isPasswordHidden {
130
+ SecureField("", text: $text, onCommit: {})
131
+ .font(fontWeight == .bold ? .action_s_bold : .body_default_regular)
132
+ .foregroundColor(getTextColor())
133
+ .disabled(disabled || readOnly)
134
+ .applyPrimaryCursorColor()
135
+ .focused($isTextFieldFocused)
136
+ .onChange(of: text) { newValue in
137
+ onChangeText?(newValue)
138
+ }
139
+ } else {
140
+ TextField("", text: $text, onEditingChanged: { focused in
141
+ handleFocusChange(focused)
142
+ })
143
+ .keyboardType(secureTextEntry ? .default : keyboardType)
144
+ .font(fontWeight == .bold ? .action_s_bold : .body_default_regular)
145
+ .foregroundColor(getTextColor())
146
+ .disabled(disabled || readOnly)
147
+ .applyPrimaryCursorColor()
148
+ .focused($isTextFieldFocused)
149
+ .onChange(of: text) { newValue in
150
+ onChangeText?(newValue)
146
151
  }
147
152
  }
148
-
149
- rightIcon
150
153
  }
151
- .padding(.horizontal, 12)
152
- .frame(height: scaleSize(size == .small ? 48 : 56, 1.1))
153
- .background(
154
- RoundedRectangle(cornerRadius: Radius.S)
155
- .fill(Colors.black01)
156
- )
157
- .overlay(RoundedRectangle(cornerRadius: 8).stroke(focused ? Colors.pink03 : Colors.border, lineWidth: 1))
158
154
 
159
- if value.isEmpty {
160
- MomoText(placeholder + (required ? " *" : ""), typography: size == .small ? .headerSSemibold : .headerMBold, color: Colors.black09)
161
- .padding(.horizontal, leadingIcon == nil ? 12 : 32)
162
- .padding(.top, 12)
163
- .allowsHitTesting(false)
155
+ // Clear button (only show when focused and has text)
156
+ if isFocused && !text.isEmpty {
157
+ SwiftUI.Button(action: { text = "" }) {
158
+ Icon(source: "24_navigation_close_circle_full", size: 16, color: Colors.black12)
159
+ .padding(.leading, Spacing.S)
160
+ }
164
161
  }
165
162
 
166
- if !floatingValue.isEmpty {
167
- FloatingComponent(floatingValue, floatingIcon: floatingIconURL.map { url in
168
- AnyView(
169
- Icon(source: url, size: 16, color: floatingIconColor)
170
- )
171
- })
172
-
163
+ // Loading indicator
164
+ if loading {
165
+ ActivityIndicator(isAnimating: .constant(true), style: .medium)
166
+ .frame(width: 16, height: 16)
167
+ .padding(.leading, Spacing.S)
168
+ }
169
+
170
+ // Right icon (password toggle or custom icon)
171
+ if secureTextEntry {
172
+ SwiftUI.Button(action: togglePasswordVisibility) {
173
+ Icon(
174
+ source: isPasswordHidden ? "24_security_eye_off" : "24_security_eye_open",
175
+ size: 24,
176
+ color: rightIconColor
177
+ )
178
+ .padding(.leading, Spacing.S)
179
+ }
180
+ } else if !rightIcon.isEmpty {
181
+ SwiftUI.Button(action: { onRightIconPressed?() }) {
182
+ Icon(source: rightIcon, size: 24, color: rightIconColor)
183
+ .padding(.leading, Spacing.S)
184
+ }
173
185
  }
174
186
  }
175
-
176
- ErrorView(
177
- errorMessage: errorMessage,
178
- errorSpacing: errorSpacing,
179
- hintText: hintText ?? ""
187
+ .padding(.horizontal, Spacing.M)
188
+ .frame(height: scaleSize(size == .small ? 48 : 56, 1.1))
189
+ .background(
190
+ RoundedRectangle(cornerRadius: Radius.S)
191
+ .fill(Colors.black01)
192
+ )
193
+ .overlay(
194
+ RoundedRectangle(cornerRadius: Radius.S)
195
+ .stroke(borderColor(), lineWidth: 1)
180
196
  )
181
197
  }
198
+
199
+ // Error or hint
200
+ ErrorView(
201
+ errorMessage: error,
202
+ errorSpacing: errorSpacing,
203
+ hintText: hintText
204
+ )
182
205
  }
206
+ .onAppear {
207
+ if autofocus {
208
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
209
+ isTextFieldFocused = true
210
+ }
211
+ }
212
+ }
213
+ .onChange(of: isTextFieldFocused) { newValue in
214
+ isFocused = newValue
215
+ if newValue {
216
+ onFocus?()
217
+ } else {
218
+ onBlur?()
219
+ }
220
+ }
221
+ }
222
+
223
+ // MARK: - Helpers
224
+
225
+ private func handleFocusChange(_ focused: Bool) {
226
+ isFocused = focused
227
+ isTextFieldFocused = focused
228
+ if focused {
229
+ onFocus?()
230
+ } else {
231
+ onBlur?()
232
+ }
233
+ }
234
+
235
+ private func togglePasswordVisibility() {
236
+ isPasswordHidden.toggle()
237
+ onRightIconPressed?()
238
+ }
239
+
240
+ private func borderColor() -> Color {
241
+ if disabled {
242
+ return Colors.black04 // border.disable
243
+ }
244
+ if !error.isEmpty {
245
+ return Colors.red03 // error.primary
246
+ }
247
+ if isFocused {
248
+ return Colors.primary
249
+ }
250
+ return Colors.black04 // border.default
251
+ }
252
+
253
+ private func getTextColor() -> Color {
254
+ return disabled ? Colors.black09 : Colors.black17
255
+ }
256
+
257
+ private func getPlaceholderColor() -> Color {
258
+ return disabled ? Colors.black09 : Colors.black12
259
+ }
260
+
261
+ private func getFloatingColor() -> Color {
262
+ return disabled ? Colors.black09 : Colors.black12
263
+ }
264
+
265
+ private func getFloatingIconColor() -> Color {
266
+ return disabled ? Colors.black09 : floatingIconColor
267
+ }
183
268
  }
184
269
 
185
270
 
271
+ // MARK: - Helper Extension
272
+ private extension View {
273
+ func applyPrimaryCursorColor() -> some View {
274
+ if #available(iOS 15.0, *) {
275
+ return self.tint(Colors.primary)
276
+ } else {
277
+ return self
278
+ }
279
+ }
280
+ }
281
+
282
+ // MARK: - Activity Indicator
186
283
  struct ActivityIndicator: UIViewRepresentable {
187
284
  @Binding var isAnimating: Bool
188
285
  let style: UIActivityIndicatorView.Style
@@ -196,6 +293,7 @@ struct ActivityIndicator: UIViewRepresentable {
196
293
  }
197
294
  }
198
295
 
296
+ // MARK: - Enums
199
297
  public enum InputSize {
200
298
  case small
201
299
  case large
@@ -17,11 +17,13 @@ public struct InputPhoneNumber: View {
17
17
  public var loading: Bool
18
18
  public var rightIcon: String
19
19
  public var rightIconColor: Color
20
+ public var autofocus: Bool
20
21
  public var onFocus: (() -> Void)?
21
22
  public var onBlur: (() -> Void)?
22
23
  public var onRightIconPressed: (() -> Void)?
23
24
 
24
25
  @State private var isFocused: Bool = false
26
+ @FocusState private var isTextFieldFocused: Bool
25
27
 
26
28
  public init(
27
29
  text: Binding<String>,
@@ -33,6 +35,7 @@ public struct InputPhoneNumber: View {
33
35
  required: Bool = false,
34
36
  rightIcon: String = "",
35
37
  rightIconColor: Color = Colors.black12,
38
+ autofocus: Bool = false,
36
39
  onFocus: (() -> Void)? = nil,
37
40
  onBlur: (() -> Void)? = nil,
38
41
  onRightIconPressed: (() -> Void)? = nil
@@ -45,6 +48,7 @@ public struct InputPhoneNumber: View {
45
48
  self.loading = loading
46
49
  self.rightIcon = rightIcon
47
50
  self.rightIconColor = rightIconColor
51
+ self.autofocus = autofocus
48
52
  self.onFocus = onFocus
49
53
  self.onBlur = onBlur
50
54
  self.onRightIconPressed = onRightIconPressed
@@ -81,6 +85,7 @@ public struct InputPhoneNumber: View {
81
85
  .foregroundColor(Colors.black17)
82
86
  .applyPrimaryCursorColor()
83
87
  .lineLimit(1)
88
+ .focused($isTextFieldFocused)
84
89
 
85
90
  }
86
91
 
@@ -124,12 +129,21 @@ public struct InputPhoneNumber: View {
124
129
  hintText: hintText
125
130
  )
126
131
  }
132
+ .onAppear {
133
+ if autofocus {
134
+ // Small delay to ensure the view is fully loaded
135
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
136
+ isTextFieldFocused = true
137
+ }
138
+ }
139
+ }
127
140
  }
128
141
 
129
142
  // MARK: - Helpers
130
143
 
131
144
  private func handleFocusChange(_ focused: Bool) {
132
145
  isFocused = focused
146
+ isTextFieldFocused = focused
133
147
  if focused {
134
148
  onFocus?()
135
149
  } else {
@@ -113,12 +113,17 @@ public struct PopupDisplay: View {
113
113
  }
114
114
 
115
115
  VStack(spacing: 0) {
116
- if(!url.isEmpty) {
116
+ if(url.isEmpty) {
117
+ Icon(source: "media_fail")
118
+ .frame(width: .infinity, height: 184)
119
+ }
120
+ else {
117
121
  WebImage(url: URL(string: url), isAnimating: .constant(true))
118
122
  .resizable()
119
123
  .placeholder {
120
124
  Rectangle()
121
125
  .fill(Color.gray.opacity(0.3))
126
+ .aspectRatio(1.777, contentMode: .fit)
122
127
  .frame(maxWidth: .infinity, maxHeight: 184)
123
128
  }
124
129
  .aspectRatio(1.777, contentMode: .fit)
@@ -49,7 +49,7 @@ public struct PopupInput: View {
49
49
  .padding(.bottom, 20)
50
50
  }.padding(.horizontal, 24)
51
51
 
52
- Input($inputValue, placeholder: inputPlaceholder, onChange: onTextChange)
52
+ Input(text: $inputValue, placeholder: inputPlaceholder, onChangeText: onTextChange)
53
53
  .padding(.horizontal, 24)
54
54
 
55
55
  if numberOfButtons == 1 {
@@ -27,12 +27,11 @@ public struct PopupPromotion: View {
27
27
  .resizable()
28
28
  .placeholder {
29
29
  Color.gray
30
- .aspectRatio(0.72, contentMode: .fit)
30
+ .aspectRatio(0.5625, contentMode: .fit)
31
31
  .frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2)
32
32
  .clipped()
33
33
  }
34
- .scaledToFill()
35
- .aspectRatio(0.72, contentMode: .fit)
34
+ .aspectRatio(0.5625, contentMode: .fit)
36
35
  .frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2, alignment: .center)
37
36
  .clipped()
38
37
  .onTapGesture {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.155.1-test.2",
3
+ "version": "0.155.1-test.20",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},