@momo-kits/native-kits 0.162.2-debug → 0.162.2-sp.1-debug

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.claude/settings.local.json +55 -0
  2. package/compose/build.gradle.kts +1 -1
  3. package/compose/compose.podspec +1 -1
  4. package/gradle.properties +1 -1
  5. package/ios/Application/Components.swift +34 -43
  6. package/ios/Application/FloatingButton.swift +10 -8
  7. package/ios/Application/Navigation/BottomTab/BottomTab.swift +102 -0
  8. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  9. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  10. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  11. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  12. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  13. package/ios/Application/Navigation/NavigationContainer.swift +151 -0
  14. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  15. package/ios/Application/Navigation/Navigator.swift +360 -0
  16. package/ios/Application/Navigation/Overplay/BottomSheet.swift +176 -0
  17. package/ios/Application/Navigation/Overplay/ModalScreen.swift +68 -0
  18. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  19. package/ios/Application/Navigation/component/Header.swift +178 -0
  20. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  21. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  22. package/ios/Application/StackScreen.swift +449 -0
  23. package/ios/Avatar/Avatar.swift +193 -0
  24. package/ios/Badge/Badge.swift +11 -13
  25. package/ios/Badge/BadgeRibbon.swift +12 -19
  26. package/ios/BaselineView/BaselineView.swift +163 -0
  27. package/ios/Button/Button.swift +62 -23
  28. package/ios/Carousel/Carousel.swift +181 -0
  29. package/ios/Checkbox/Checkbox.swift +14 -12
  30. package/ios/Chip/Chip.swift +23 -9
  31. package/ios/Collapse/Collapse.swift +182 -0
  32. package/ios/Colors+Radius+Spacing/Colors.swift +1 -1
  33. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  34. package/ios/DateTimePicker/DateTimePicker.swift +210 -0
  35. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  36. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  37. package/ios/DateTimePicker/WheelPicker.swift +161 -0
  38. package/ios/Divider/Divider.swift +16 -0
  39. package/ios/Icon/Icon.swift +6 -1
  40. package/ios/IconButton/IconButton.swift +112 -0
  41. package/ios/Information/Information.swift +151 -0
  42. package/ios/Input/Input.swift +42 -25
  43. package/ios/Input/InputPhoneNumber.swift +21 -19
  44. package/ios/Input/InputSearch.swift +101 -45
  45. package/ios/Input/InputTextArea.swift +67 -40
  46. package/ios/InputDropDown/InputDropDown.swift +198 -0
  47. package/ios/InputMoney/InputMoney.swift +335 -0
  48. package/ios/InputOTP/InputOTP.swift +210 -0
  49. package/ios/Loader/Loader.swift +113 -0
  50. package/ios/MoMoUIKits.podspec +5 -0
  51. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  52. package/ios/Pagination/PaginationDot.swift +61 -0
  53. package/ios/Pagination/PaginationNumber.swift +35 -0
  54. package/ios/Pagination/PaginationScroll.swift +101 -0
  55. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  56. package/ios/Popup/PopupDisplay.swift +10 -12
  57. package/ios/Popup/PopupInput.swift +2 -6
  58. package/ios/Popup/PopupNotify.swift +219 -0
  59. package/ios/ProgressInfo/ProgressInfo.swift +294 -0
  60. package/ios/Radio/Radio.swift +70 -0
  61. package/ios/Rating/Rating.swift +82 -0
  62. package/ios/Skeleton/Skeleton.swift +99 -0
  63. package/ios/Slider/Slider.swift +237 -0
  64. package/ios/Stepper/Stepper.swift +220 -0
  65. package/ios/Steps/Steps.swift +451 -0
  66. package/ios/SuggestAction/SuggestAction.swift +97 -0
  67. package/ios/Swipeable/SwipeCell.swift +185 -0
  68. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  69. package/ios/TabView/TabView.swift +530 -0
  70. package/ios/Tag/Tag.swift +98 -0
  71. package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
  72. package/ios/Title/Title.swift +269 -0
  73. package/ios/Tooltip/Tooltip.swift +482 -0
  74. package/ios/Typography/Text.swift +77 -5
  75. package/ios/Typography/Typography.swift +9 -0
  76. package/ios/Uploader/Uploader.swift +183 -0
  77. package/ios/native-kits.podspec +58 -8
  78. package/ios-demo/MoMoUIKitsDemo.podspec +18 -0
  79. package/ios-demo/README.md +120 -0
  80. package/ios-demo/Resources/dot_loading.json +558 -0
  81. package/ios-demo/Resources/icon.json +4052 -0
  82. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  83. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  84. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  85. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  86. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  87. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  88. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  89. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  90. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  91. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  92. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  93. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +72 -0
  94. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  95. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  96. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  97. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  98. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  99. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  100. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  101. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +63 -0
  102. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  103. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  104. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  105. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  106. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  107. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  108. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +261 -0
  109. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  110. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  111. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  112. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  113. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  114. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  115. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  116. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  117. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  118. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  119. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  120. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +227 -0
  121. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  122. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  123. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  124. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  125. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  126. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  127. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  128. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  129. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  130. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  131. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  132. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  133. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  134. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  135. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  136. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  137. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  138. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  139. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  140. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  141. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  142. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  143. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +50 -0
  144. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  145. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  146. package/local.properties +8 -0
  147. package/package.json +1 -1
  148. package/publish.sh +50 -0
  149. package/scripts/gen-ios-color.mjs +136 -0
@@ -9,11 +9,12 @@ import SwiftUI
9
9
  // MARK: - RenderRightIconSearch
10
10
 
11
11
  struct RenderRightIconSearch: View {
12
+ @Environment(\.appTheme) private var theme
12
13
  let loading: Bool
13
14
  let icon: String
14
15
  let color: Color
15
16
  let onClick: () -> Void
16
-
17
+
17
18
  var body: some View {
18
19
  HStack(spacing: 0) {
19
20
  if loading {
@@ -24,12 +25,10 @@ struct RenderRightIconSearch: View {
24
25
  if !icon.isEmpty {
25
26
  Divider()
26
27
  .frame(width: 1)
27
- .background(Colors.primary)
28
+ .background(theme.colors.primary)
28
29
  .padding(.leading, Spacing.S)
29
30
 
30
- Image(systemName: icon)
31
- .foregroundColor(color)
32
- .frame(width: 24, height: 24)
31
+ Icon(source: icon, size: 24, color: color)
33
32
  .padding(.leading, Spacing.S)
34
33
  .onTapGesture(perform: onClick)
35
34
  }
@@ -48,14 +47,18 @@ public struct InputSearchProps {
48
47
  var error: String = ""
49
48
  var disabled: Bool = false
50
49
  var icon: String = ""
51
- var iconColor: Color = Colors.text
50
+ var iconColor: Color? = nil
52
51
  var onRightIconPressed: () -> Void = {}
53
52
  var onFocus: () -> Void = {}
54
53
  var onBlur: () -> Void = {}
55
54
  var loading: Bool = false
56
55
  var fontWeight: Font.Weight = .regular
57
56
  var keyboardType: UIKeyboardType = .default
58
-
57
+ // Mirrors Compose's InputSearchProps.searchIcon — a custom leading icon view.
58
+ var searchIcon: (() -> AnyView)? = nil
59
+ // Mirrors Compose's InputSearchProps.placeholderCustomRender — a custom placeholder view.
60
+ var placeholderCustomRender: (() -> AnyView)? = nil
61
+
59
62
  public init(text: Binding<String>,
60
63
  buttonText: String = "",
61
64
  showButtonText: Bool = false,
@@ -66,13 +69,15 @@ public struct InputSearchProps {
66
69
  error: String = "",
67
70
  disabled: Bool = false,
68
71
  icon: String = "",
69
- iconColor: Color = Colors.text,
72
+ iconColor: Color? = nil,
70
73
  onRightIconPressed: @escaping () -> Void = {},
71
74
  onFocus: @escaping () -> Void = {},
72
75
  onBlur: @escaping () -> Void = {},
73
76
  loading: Bool = false,
74
77
  fontWeight: Font.Weight = .regular,
75
- keyboardType: UIKeyboardType = .default) {
78
+ keyboardType: UIKeyboardType = .default,
79
+ searchIcon: (() -> AnyView)? = nil,
80
+ placeholderCustomRender: (() -> AnyView)? = nil) {
76
81
  self._text = text
77
82
  self.buttonText = buttonText
78
83
  self.showButtonText = showButtonText
@@ -90,12 +95,15 @@ public struct InputSearchProps {
90
95
  self.loading = loading
91
96
  self.fontWeight = fontWeight
92
97
  self.keyboardType = keyboardType
98
+ self.searchIcon = searchIcon
99
+ self.placeholderCustomRender = placeholderCustomRender
93
100
  }
94
101
  }
95
102
 
96
103
  // MARK: - InputSearch
97
104
 
98
105
  public struct InputSearch: View {
106
+ @Environment(\.appTheme) private var theme
99
107
  @Binding var text: String
100
108
  var buttonText: String = ""
101
109
  var showButtonText: Bool = false
@@ -106,17 +114,19 @@ public struct InputSearch: View {
106
114
  var error: String = ""
107
115
  var disabled: Bool = false
108
116
  var icon: String = ""
109
- var iconColor: Color = Colors.text
117
+ var iconColor: Color? = nil
110
118
  var onRightIconPressed: () -> Void = {}
111
119
  var onFocus: () -> Void = {}
112
120
  var onBlur: () -> Void = {}
113
121
  var loading: Bool = false
114
122
  var fontWeight: Font.Weight = .regular
115
123
  var keyboardType: UIKeyboardType = .default
116
-
124
+ var searchIcon: (() -> AnyView)? = nil
125
+ var placeholderCustomRender: (() -> AnyView)? = nil
126
+
117
127
  @State private var isFocused = false
118
128
  @State private var isBlurred = false
119
-
129
+
120
130
  public init(text: Binding<String>,
121
131
  buttonText: String = "",
122
132
  showButtonText: Bool = false,
@@ -127,13 +137,15 @@ public struct InputSearch: View {
127
137
  error: String = "",
128
138
  disabled: Bool = false,
129
139
  icon: String = "",
130
- iconColor: Color = Colors.text,
140
+ iconColor: Color? = nil,
131
141
  onRightIconPressed: @escaping () -> Void = {},
132
142
  onFocus: @escaping () -> Void = {},
133
143
  onBlur: @escaping () -> Void = {},
134
144
  loading: Bool = false,
135
145
  fontWeight: Font.Weight = .regular,
136
- keyboardType: UIKeyboardType = .default) {
146
+ keyboardType: UIKeyboardType = .default,
147
+ searchIcon: (() -> AnyView)? = nil,
148
+ placeholderCustomRender: (() -> AnyView)? = nil) {
137
149
  self._text = text
138
150
  self.buttonText = buttonText
139
151
  self.showButtonText = showButtonText
@@ -151,23 +163,60 @@ public struct InputSearch: View {
151
163
  self.loading = loading
152
164
  self.fontWeight = fontWeight
153
165
  self.keyboardType = keyboardType
166
+ self.searchIcon = searchIcon
167
+ self.placeholderCustomRender = placeholderCustomRender
154
168
  }
155
169
 
156
-
170
+ /// Convenience initializer mirroring Compose's `InputSearch(inputSearchProps)`.
171
+ public init(props: InputSearchProps) {
172
+ self.init(
173
+ text: props.$text,
174
+ buttonText: props.buttonText,
175
+ showButtonText: props.showButtonText,
176
+ showBorder: props.showBorder,
177
+ placeholder: props.placeholder,
178
+ onChangeText: props.onChangeText,
179
+ onPressButtonText: props.onPressButtonText,
180
+ error: props.error,
181
+ disabled: props.disabled,
182
+ icon: props.icon,
183
+ iconColor: props.iconColor,
184
+ onRightIconPressed: props.onRightIconPressed,
185
+ onFocus: props.onFocus,
186
+ onBlur: props.onBlur,
187
+ loading: props.loading,
188
+ fontWeight: props.fontWeight,
189
+ keyboardType: props.keyboardType,
190
+ searchIcon: props.searchIcon,
191
+ placeholderCustomRender: props.placeholderCustomRender
192
+ )
193
+ }
194
+
195
+
157
196
  public var body: some View {
197
+ let iconColor: Color = disabled ? theme.colors.text.disable : (self.iconColor ?? theme.colors.text.default)
158
198
  VStack(alignment: .leading, spacing: 0) {
159
199
  HStack(spacing: 0) {
160
200
  HStack {
161
- Image(systemName: "magnifyingglass")
162
- .foregroundColor(Colors.black12)
163
- .frame(width: 24, height: 24)
164
- .padding(.trailing, Spacing.XS)
165
-
201
+ if let searchIcon = searchIcon {
202
+ searchIcon()
203
+ } else {
204
+ Image(systemName: "magnifyingglass")
205
+ .foregroundColor(theme.colors.text.hint)
206
+ .frame(width: 24, height: 24)
207
+ .padding(.trailing, Spacing.XS)
208
+ }
209
+
166
210
  ZStack(alignment: .leading) {
167
211
  if text.isEmpty {
168
- Text(placeholder)
169
- .foregroundColor(disabled ? Colors.black08 : Colors.black12)
170
- .font(.system(size: 16, weight: fontWeight))
212
+ if let placeholderCustomRender = placeholderCustomRender {
213
+ placeholderCustomRender()
214
+ } else {
215
+ Text(placeholder)
216
+ .foregroundColor(disabled ? theme.colors.text.disable : theme.colors.text.hint)
217
+ .font(.system(size: scaleSize(14), weight: fontWeight))
218
+ .ignoreBoldTextSetting()
219
+ }
171
220
  }
172
221
  TextField("", text: $text, onEditingChanged: { editing in
173
222
  isFocused = editing
@@ -181,25 +230,27 @@ public struct InputSearch: View {
181
230
  }
182
231
  }, onCommit: {})
183
232
  .disabled(disabled)
184
- .foregroundColor(disabled ? Colors.black08 : Colors.black17)
185
- .font(.system(size: 15, weight: fontWeight))
233
+ .foregroundColor(disabled ? theme.colors.text.disable : theme.colors.text.default)
234
+ .font(.system(size: scaleSize(14), weight: fontWeight))
235
+ .ignoreBoldTextSetting()
186
236
  .keyboardType(keyboardType)
237
+ .applyCursorColor(theme.colors.primary)
187
238
  }
188
239
 
189
240
  if isFocused && !text.isEmpty {
190
241
  SwiftButton(action: { text = "" }) {
191
242
  Image(systemName: "xmark.circle.fill")
192
- .foregroundColor(Colors.black12)
243
+ .foregroundColor(theme.colors.text.hint)
193
244
  .frame(width: 16, height: 16)
194
245
  }
195
246
  .padding(.leading, Spacing.S)
196
247
  }
197
248
 
198
- RenderRightIconSearch(loading: loading, icon: icon, color: disabled ? Colors.black08 : iconColor, onClick: onRightIconPressed)
249
+ RenderRightIconSearch(loading: loading, icon: icon, color: iconColor, onClick: onRightIconPressed)
199
250
  }
200
251
  .padding(.horizontal, Spacing.M)
201
252
  .padding(.vertical, Spacing.S)
202
- .background(Colors.black01)
253
+ .background(theme.colors.background.surface)
203
254
  .cornerRadius(Radius.XL)
204
255
  .overlay(
205
256
  RoundedRectangle(cornerRadius: Radius.XL)
@@ -209,30 +260,35 @@ public struct InputSearch: View {
209
260
 
210
261
  if showButtonText {
211
262
  SwiftButton(action: onPressButtonText) {
212
- Text(buttonText)
213
- .foregroundColor(Colors.black17)
214
- .font(.system(size: 14, weight: .medium))
263
+ MomoText(buttonText, typography: .actionDefaultBold, color: theme.colors.text.default)
215
264
  }
216
265
  .padding(.leading, Spacing.L)
217
266
  }
218
267
  }
219
268
  }
220
269
  }
221
- }
222
270
 
223
- // MARK: - Helper Functions
224
-
225
- func getBorderColor(isFocused: Bool, error: String, disabled: Bool) -> Color {
226
- if disabled {
227
- return Colors.black08
228
- } else if !error.isEmpty {
229
- return Colors.red03
230
- } else if isFocused {
231
- return Colors.primary
232
- } else {
233
- return Colors.black04
271
+ // MARK: - Helper Functions
272
+
273
+ func getBorderColor(isFocused: Bool, error: String, disabled: Bool) -> Color {
274
+ if disabled {
275
+ return theme.colors.border.disable
276
+ } else if !error.isEmpty {
277
+ return theme.colors.error.primary
278
+ } else if isFocused {
279
+ return theme.colors.primary
280
+ } else {
281
+ return theme.colors.border.default
282
+ }
234
283
  }
235
284
  }
236
285
 
237
-
238
-
286
+ private extension View {
287
+ func applyCursorColor(_ color: Color) -> some View {
288
+ if #available(iOS 15.0, *) {
289
+ return self.tint(color)
290
+ } else {
291
+ return self
292
+ }
293
+ }
294
+ }
@@ -12,6 +12,12 @@ public let DEFAULT_HEIGHT: CGFloat = 104
12
12
 
13
13
  struct UITextViewWrapper: UIViewRepresentable {
14
14
  @Binding var text: String
15
+ var maxLength: Int
16
+ var textColor: Color
17
+ var cursorColor: Color
18
+ var fontWeight: Font.Weight
19
+ var disabled: Bool
20
+ var onChangeText: (String) -> Void
15
21
  var onDone: (() -> Void)?
16
22
  var onFocus: (() -> Void)?
17
23
  var onBlur: (() -> Void)?
@@ -19,11 +25,20 @@ struct UITextViewWrapper: UIViewRepresentable {
19
25
  func makeUIView(context: Context) -> UITextView {
20
26
  let textView = UITextView()
21
27
  textView.delegate = context.coordinator
28
+ textView.backgroundColor = .clear
29
+ textView.textContainerInset = .zero
30
+ textView.textContainer.lineFragmentPadding = 0
22
31
  return textView
23
32
  }
24
33
 
25
34
  func updateUIView(_ uiView: UITextView, context: Context) {
26
- uiView.text = text
35
+ if uiView.text != text {
36
+ uiView.text = text
37
+ }
38
+ uiView.textColor = UIColor(textColor)
39
+ uiView.tintColor = UIColor(cursorColor)
40
+ uiView.font = UIFont.systemFont(ofSize: scaleSize(16), weight: fontWeight == .bold ? .bold : .regular)
41
+ uiView.isEditable = !disabled
27
42
  }
28
43
 
29
44
  func makeCoordinator() -> Coordinator {
@@ -38,7 +53,12 @@ struct UITextViewWrapper: UIViewRepresentable {
38
53
  }
39
54
 
40
55
  func textViewDidChange(_ textView: UITextView) {
41
- parent.text = textView.text
56
+ let limited = String(textView.text.prefix(parent.maxLength))
57
+ if textView.text != limited {
58
+ textView.text = limited
59
+ }
60
+ parent.text = limited
61
+ parent.onChangeText(limited)
42
62
  }
43
63
 
44
64
  func textViewDidBeginEditing(_ textView: UITextView) {
@@ -54,27 +74,31 @@ struct UITextViewWrapper: UIViewRepresentable {
54
74
  parent.onDone?()
55
75
  return false
56
76
  }
57
- return true
77
+ let current = textView.text ?? ""
78
+ guard let range = Range(range, in: current) else { return false }
79
+ let proposed = current.replacingCharacters(in: range, with: text)
80
+ return proposed.count <= parent.maxLength
58
81
  }
59
82
  }
60
83
  }
61
84
 
62
85
  // Make the struct public
63
86
  public struct InputTextArea: View {
87
+ @Environment(\.appTheme) private var theme
64
88
  @Binding var text: String
65
89
  var maxLength: Int = MAX_LENGTH
66
90
  var height: CGFloat = DEFAULT_HEIGHT
67
91
  var floatingValue: String = ""
68
- var floatingValueColor: Color = .gray
92
+ var floatingValueColor: Color?
69
93
  var floatingIcon: String = ""
70
- var floatingIconColor: Color = .black
94
+ var floatingIconColor: Color?
71
95
  var placeholder: String = ""
72
96
  var onChangeText: (String) -> Void = { _ in }
73
97
  var error: String = ""
74
98
  var errorSpacing: Bool = false
75
99
  var disabled: Bool = false
76
100
  var icon: String = ""
77
- var iconColor: Color = .black
101
+ var iconColor: Color?
78
102
  var onRightIconPressed: () -> Void = {}
79
103
  var onFocus: () -> Void = {}
80
104
  var onBlur: () -> Void = {}
@@ -92,16 +116,16 @@ public struct InputTextArea: View {
92
116
  maxLength: Int = MAX_LENGTH,
93
117
  height: CGFloat = DEFAULT_HEIGHT,
94
118
  floatingValue: String = "",
95
- floatingValueColor: Color = .gray,
119
+ floatingValueColor: Color? = nil,
96
120
  floatingIcon: String = "",
97
- floatingIconColor: Color = .black,
121
+ floatingIconColor: Color? = nil,
98
122
  placeholder: String = "",
99
123
  onChangeText: @escaping (String) -> Void = { _ in },
100
124
  error: String = "",
101
125
  errorSpacing: Bool = false,
102
126
  disabled: Bool = false,
103
127
  icon: String = "",
104
- iconColor: Color = Colors.pink03,
128
+ iconColor: Color? = nil,
105
129
  onRightIconPressed: @escaping () -> Void = {},
106
130
  onFocus: @escaping () -> Void = {},
107
131
  onBlur: @escaping () -> Void = {},
@@ -138,24 +162,27 @@ public struct InputTextArea: View {
138
162
  ZStack(alignment: .topLeading) {
139
163
  if !floatingValue.isEmpty || !floatingIcon.isEmpty {
140
164
  HStack(spacing: 4) {
141
- Text(floatingValue)
142
- .font(.caption)
143
- .foregroundColor(disabled ? .gray : floatingValueColor)
165
+ MomoText(
166
+ floatingValue,
167
+ typography: .labelSMedium,
168
+ color: disabled ? theme.colors.text.disable : (floatingValueColor ?? theme.colors.text.hint)
169
+ )
144
170
 
145
171
  if required {
146
- Text("*")
147
- .font(.caption)
148
- .foregroundColor(.red)
172
+ MomoText("*", typography: .labelSMedium, color: theme.colors.error.primary)
149
173
  }
150
174
 
151
175
  if !floatingIcon.isEmpty {
152
- Image(systemName: floatingIcon)
153
- .font(.caption)
154
- .foregroundColor(disabled ? .gray : floatingIconColor)
176
+ Icon(
177
+ source: floatingIcon,
178
+ size: 16,
179
+ color: disabled ? theme.colors.text.disable : (floatingIconColor ?? theme.colors.text.default)
180
+ )
181
+ .padding(.leading, Spacing.XS)
155
182
  }
156
183
  }
157
- .padding(.horizontal, 8)
158
- .background(Colors.black01)
184
+ .padding(.horizontal, Spacing.S)
185
+ .background(theme.colors.background.surface)
159
186
  .offset(x: Spacing.S, y: -height / 10)
160
187
  .zIndex(10)
161
188
  }
@@ -164,13 +191,21 @@ public struct InputTextArea: View {
164
191
  ZStack(alignment: .topLeading) {
165
192
  if text.isEmpty {
166
193
  Text(placeholder)
167
- .foregroundColor(disabled ? .gray : .gray)
194
+ .foregroundColor(disabled ? theme.colors.text.disable : theme.colors.text.hint)
168
195
  .font(.system(size: 16, weight: fontWeight))
169
196
  .padding(.horizontal, 16)
170
197
  .padding(.vertical, 12)
171
198
  }
172
199
 
173
- UITextViewWrapper(text: $text, onDone: {
200
+ UITextViewWrapper(
201
+ text: $text,
202
+ maxLength: maxLength,
203
+ textColor: disabled ? theme.colors.text.disable : theme.colors.text.default,
204
+ cursorColor: theme.colors.primary,
205
+ fontWeight: fontWeight,
206
+ disabled: disabled,
207
+ onChangeText: onChangeText,
208
+ onDone: {
174
209
  // Handle done action if needed
175
210
  },
176
211
  onFocus: {
@@ -188,27 +223,20 @@ public struct InputTextArea: View {
188
223
 
189
224
  HStack {
190
225
  Spacer()
191
- Text("\(text.count)/\(maxLength)")
192
- .font(.caption)
193
- .foregroundColor(.gray)
226
+ MomoText("\(text.count)/\(maxLength)", typography: .descriptionXsRegular, color: theme.colors.text.hint)
194
227
  }
195
228
  .padding(.horizontal, 16)
196
229
  .padding(.bottom, 12)
197
230
  }
198
- .background(Color.white)
199
- .cornerRadius(8)
231
+ .background(theme.colors.background.surface)
232
+ .cornerRadius(Radius.S)
200
233
  .overlay(
201
- RoundedRectangle(cornerRadius: 8)
234
+ RoundedRectangle(cornerRadius: Radius.S)
202
235
  .stroke(borderColor, lineWidth: 1)
203
236
  )
204
237
  }
205
238
 
206
- if !error.isEmpty {
207
- Text(error)
208
- .font(.caption)
209
- .foregroundColor(.red)
210
- .padding(.top, errorSpacing ? 8 : 4)
211
- }
239
+ ErrorView(errorMessage: error, errorSpacing: errorSpacing, hintText: "")
212
240
  }
213
241
  .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in
214
242
  if !isBlurred {
@@ -223,20 +251,19 @@ public struct InputTextArea: View {
223
251
  onBlur()
224
252
  }
225
253
  }
254
+ .ignoreBoldTextSetting()
226
255
  }
227
256
 
228
257
  private var borderColor: Color {
229
258
  if disabled {
230
- return Colors.black03
259
+ return theme.colors.border.disable
231
260
  } else if !error.isEmpty {
232
- return .red
261
+ return theme.colors.error.primary
233
262
  } else if isFocused {
234
- return Colors.pink03
263
+ return theme.colors.primary
235
264
  } else {
236
- return Colors.border
265
+ return theme.colors.border.default
237
266
  }
238
267
 
239
268
  }
240
269
  }
241
-
242
-