@momo-kits/native-kits 0.152.4-beta.1 → 0.152.4-beta.10

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 (157) hide show
  1. package/CODE_OF_CONDUCT.md +133 -0
  2. package/CONTRIBUTING.md +114 -0
  3. package/LICENSE +20 -0
  4. package/README.md +7 -0
  5. package/build.gradle.kts +32 -0
  6. package/compose/MoMoComposeKits.podspec +54 -0
  7. package/compose/build.gradle.kts +149 -0
  8. package/compose/src/androidMain/AndroidManifest.xml +2 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +105 -0
  10. package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +1 -0
  11. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  12. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  23. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  24. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +306 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +715 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +214 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +236 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +77 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +334 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +345 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +90 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +131 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +58 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +143 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +179 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +111 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +384 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +160 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +234 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +223 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +232 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +236 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +228 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +50 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +34 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +85 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +33 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +338 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +95 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +64 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +89 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +84 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +208 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +172 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +199 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +237 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +191 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +258 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +49 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +232 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +111 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +94 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +159 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +232 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +17 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +459 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +169 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +216 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +180 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +251 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +385 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +38 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +62 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
  108. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +144 -0
  109. package/gradle.properties +19 -0
  110. package/gradlew +240 -0
  111. package/gradlew.bat +91 -0
  112. package/ios/Application/ApplicationEnvironment.swift +50 -0
  113. package/ios/Application/Components.swift +263 -0
  114. package/ios/Application/ComposeApi.swift +22 -0
  115. package/ios/Application/FloatingButton.swift +172 -0
  116. package/ios/Application/HeaderRight.swift +271 -0
  117. package/ios/Application/Screen.swift +249 -0
  118. package/ios/Badge/Badge.swift +85 -0
  119. package/ios/Badge/BadgeDot.swift +31 -0
  120. package/ios/Badge/BadgeRibbon.swift +174 -0
  121. package/ios/Button/Button.swift +211 -0
  122. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
  123. package/ios/Checkbox/Checkbox.swift +81 -0
  124. package/ios/Chip/Chip.swift +96 -0
  125. package/ios/Colors+Radius+Spacing/Colors.swift +172 -0
  126. package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
  127. package/ios/Colors+Radius+Spacing/Spacing.swift +12 -0
  128. package/ios/Extensions/Color++.swift +25 -0
  129. package/ios/Icon/Icon.swift +51 -0
  130. package/ios/Image/Image.swift +70 -0
  131. package/ios/Input/Input.swift +207 -0
  132. package/ios/Input/InputPhoneNumber.swift +176 -0
  133. package/ios/Input/InputSearch.swift +238 -0
  134. package/ios/Input/InputTextArea.swift +242 -0
  135. package/ios/Lottie/LottieView.swift +86 -0
  136. package/ios/OTPKeyboard/KeyboardButton.swift +41 -0
  137. package/ios/OTPKeyboard/OTPKeyboard.swift +145 -0
  138. package/ios/Popup/PopupDisplay.swift +284 -0
  139. package/ios/Popup/PopupInput.swift +96 -0
  140. package/ios/Popup/PopupPromotion.swift +73 -0
  141. package/ios/PopupView/FullscreenPopup.swift +251 -0
  142. package/ios/PopupView/Modifiers.swift +158 -0
  143. package/ios/PopupView/PopupView.swift +289 -0
  144. package/ios/PopupView/Utils++.swift +281 -0
  145. package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
  146. package/ios/Swipeable/SwipeCell.swift +278 -0
  147. package/ios/Swipeable/SwipeCellModel.swift +86 -0
  148. package/ios/Switch/Switch.swift +44 -0
  149. package/ios/Template/Logo/Logo.swift +75 -0
  150. package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
  151. package/ios/Theme.md +18 -0
  152. package/ios/Typography/Text.swift +140 -0
  153. package/ios/Typography/Typography.swift +95 -0
  154. package/ios/native-kits.podspec +18 -0
  155. package/package.json +6 -7
  156. package/settings.gradle.kts +25 -0
  157. package/shared/build.gradle.kts +0 -74
@@ -0,0 +1,176 @@
1
+ //
2
+ // InputPhoneNumber.swift
3
+ // Pods
4
+ //
5
+ // Created by sophia on 20/10/25.
6
+ //
7
+
8
+ import SwiftUI
9
+
10
+ public struct InputPhoneNumber: View {
11
+ @Binding public var text: String
12
+
13
+ public var placeholder: String
14
+ public var size: InputSize
15
+ public var hintText: String
16
+ public var error: String
17
+ public var loading: Bool
18
+ public var rightIcon: String
19
+ public var rightIconColor: Color
20
+ public var onFocus: (() -> Void)?
21
+ public var onBlur: (() -> Void)?
22
+ public var onRightIconPressed: (() -> Void)?
23
+
24
+ @State private var isFocused: Bool = false
25
+
26
+ public init(
27
+ text: Binding<String>,
28
+ placeholder: String = "0123456789",
29
+ size: InputSize = .small,
30
+ hintText: String = "",
31
+ error: String = "",
32
+ loading: Bool = false,
33
+ required: Bool = false,
34
+ rightIcon: String = "",
35
+ rightIconColor: Color = Colors.black12,
36
+ onFocus: (() -> Void)? = nil,
37
+ onBlur: (() -> Void)? = nil,
38
+ onRightIconPressed: (() -> Void)? = nil
39
+ ) {
40
+ self._text = text
41
+ self.placeholder = placeholder
42
+ self.size = size
43
+ self.hintText = hintText
44
+ self.error = error
45
+ self.loading = loading
46
+ self.rightIcon = rightIcon
47
+ self.rightIconColor = rightIconColor
48
+ self.onFocus = onFocus
49
+ self.onBlur = onBlur
50
+ self.onRightIconPressed = onRightIconPressed
51
+ }
52
+
53
+ // MARK: - Body
54
+ public var body: some View {
55
+ VStack(alignment: .leading, spacing: 4) {
56
+ HStack(spacing: 0) {
57
+ // 🇻🇳 Flag
58
+ ImageView("https://static.momocdn.net/app/img/icon/ic-qrcode-package/ic_vn_flag.png")
59
+ .frame(width: 24, height: 24)
60
+ .padding(.trailing, Spacing.XS)
61
+
62
+ MomoText("+84", typography: size == .small ? .headerSSemibold : .headerMBold)
63
+ .foregroundColor(Colors.black17)
64
+
65
+ Rectangle()
66
+ .fill(Colors.black04)
67
+ .frame(width: 1, height: size == .small ? 24 : 32)
68
+ .padding(.horizontal, Spacing.M)
69
+
70
+ // Text input
71
+ ZStack(alignment: .leading) {
72
+ if text.isEmpty {
73
+ Text(placeholder)
74
+ .foregroundColor(Colors.black12)
75
+ .font(size == .small ? .header_s_semibold : .header_m_bold)
76
+ }
77
+
78
+ TextField("", text: $text, onEditingChanged: { focused in
79
+ handleFocusChange(focused)
80
+ })
81
+ .keyboardType(.numberPad)
82
+ .font(size == .small ? .header_s_semibold : .header_m_bold)
83
+ .foregroundColor(Colors.black17)
84
+ .applyPrimaryCursorColor()
85
+ .lineLimit(1)
86
+
87
+ }
88
+
89
+ // Clear button
90
+ if isFocused && !text.isEmpty {
91
+ SwiftUI.Button(action: { text = "" }) {
92
+ Icon(source: "24_navigation_close_circle_full", size: 16, color: Colors.black12)
93
+ .padding(.leading, Spacing.S)
94
+ }
95
+ }
96
+
97
+ // Loading indicator
98
+ if loading {
99
+ if #available(iOS 14.0, *) {
100
+ ProgressView()
101
+ .progressViewStyle(CircularProgressViewStyle(tint: Colors.black17))
102
+ .frame(width: 16, height: 16)
103
+ .padding(.leading, Spacing.S)
104
+ } else {
105
+ Image(systemName: "clock")
106
+ .rotationEffect(.degrees(isFocused ? 360 : 0))
107
+ .animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
108
+ .frame(width: 16, height: 16)
109
+ .padding(.leading, Spacing.S)
110
+ }
111
+ }
112
+
113
+ // ✅ Right icon
114
+ if !rightIcon.isEmpty {
115
+ SwiftUI.Button(action: { onRightIconPressed?() }) {
116
+ Icon(source: rightIcon, size: 24, color: rightIconColor)
117
+ .padding(.leading, Spacing.S)
118
+ }
119
+ }
120
+ }
121
+ .padding(.horizontal, Spacing.M)
122
+ .frame(height: size == .small ? 48 : 56)
123
+ .overlay(
124
+ RoundedRectangle(cornerRadius: Radius.S)
125
+ .stroke(borderColor(), lineWidth: isFocused ? 1.5 : 1)
126
+ )
127
+
128
+ // Error or hint
129
+ if (!error.isEmpty || !hintText.isEmpty) {
130
+ HStack(spacing: 4) {
131
+ Icon(source: "ic_error", size: 16, color: errorColor())
132
+ MomoText(
133
+ error.isEmpty ? (hintText.isEmpty ? "Không thể chỉnh sửa" : hintText) : error,
134
+ typography: .descriptionDefaultRegular,
135
+ color: errorColor()
136
+ )
137
+ }
138
+ .padding(.top, Spacing.XS)
139
+ }
140
+ }
141
+ }
142
+
143
+ // MARK: - Helpers
144
+
145
+ private func handleFocusChange(_ focused: Bool) {
146
+ isFocused = focused
147
+ if focused {
148
+ onFocus?()
149
+ } else {
150
+ onBlur?()
151
+ }
152
+ }
153
+
154
+ private func borderColor() -> Color {
155
+ if !error.isEmpty { return Colors.red03 }
156
+ if isFocused { return Colors.primary }
157
+ return Colors.black04
158
+ }
159
+
160
+ private func errorColor() -> Color {
161
+ !error.isEmpty ? Colors.red03 : Colors.black12
162
+ }
163
+ }
164
+
165
+ private extension View {
166
+ func applyPrimaryCursorColor() -> some View {
167
+ if #available(iOS 15.0, *) {
168
+ // Modern SwiftUI: use .tint() for cursor
169
+ return self.tint(Colors.primary)
170
+ } else {
171
+ // iOS 13–14: return unchanged (no tint support)
172
+ return self
173
+ }
174
+ }
175
+ }
176
+
@@ -0,0 +1,238 @@
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
+ 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
+
96
+ // MARK: - InputSearch
97
+
98
+ public struct InputSearch: View {
99
+ @Binding var text: String
100
+ var buttonText: String = ""
101
+ var showButtonText: Bool = false
102
+ var showBorder: Bool = true
103
+ var placeholder: String = ""
104
+ var onChangeText: (String) -> Void = { _ in }
105
+ var onPressButtonText: () -> Void = {}
106
+ var error: String = ""
107
+ var disabled: Bool = false
108
+ var icon: String = ""
109
+ var iconColor: Color = Colors.text
110
+ var onRightIconPressed: () -> Void = {}
111
+ var onFocus: () -> Void = {}
112
+ var onBlur: () -> Void = {}
113
+ var loading: Bool = false
114
+ var fontWeight: Font.Weight = .regular
115
+ var keyboardType: UIKeyboardType = .default
116
+
117
+ @State private var isFocused = false
118
+ @State private var isBlurred = false
119
+
120
+ public init(text: Binding<String>,
121
+ buttonText: String = "",
122
+ showButtonText: Bool = false,
123
+ showBorder: Bool = true,
124
+ placeholder: String = "",
125
+ onChangeText: @escaping (String) -> Void = { _ in },
126
+ onPressButtonText: @escaping () -> Void = {},
127
+ error: String = "",
128
+ disabled: Bool = false,
129
+ icon: String = "",
130
+ iconColor: Color = Colors.text,
131
+ onRightIconPressed: @escaping () -> Void = {},
132
+ onFocus: @escaping () -> Void = {},
133
+ onBlur: @escaping () -> Void = {},
134
+ loading: Bool = false,
135
+ fontWeight: Font.Weight = .regular,
136
+ keyboardType: UIKeyboardType = .default) {
137
+ self._text = text
138
+ self.buttonText = buttonText
139
+ self.showButtonText = showButtonText
140
+ self.showBorder = showBorder
141
+ self.placeholder = placeholder
142
+ self.onChangeText = onChangeText
143
+ self.onPressButtonText = onPressButtonText
144
+ self.error = error
145
+ self.disabled = disabled
146
+ self.icon = icon
147
+ self.iconColor = iconColor
148
+ self.onRightIconPressed = onRightIconPressed
149
+ self.onFocus = onFocus
150
+ self.onBlur = onBlur
151
+ self.loading = loading
152
+ self.fontWeight = fontWeight
153
+ self.keyboardType = keyboardType
154
+ }
155
+
156
+
157
+ public var body: some View {
158
+ VStack(alignment: .leading, spacing: 0) {
159
+ HStack(spacing: 0) {
160
+ HStack {
161
+ Image(systemName: "magnifyingglass")
162
+ .foregroundColor(Colors.black12)
163
+ .frame(width: 24, height: 24)
164
+ .padding(.trailing, Spacing.XS)
165
+
166
+ ZStack(alignment: .leading) {
167
+ if text.isEmpty {
168
+ Text(placeholder)
169
+ .foregroundColor(disabled ? Colors.black08 : Colors.black12)
170
+ .font(.system(size: 16, weight: fontWeight))
171
+ }
172
+ TextField("", text: $text, onEditingChanged: { editing in
173
+ isFocused = editing
174
+ if editing {
175
+ onFocus()
176
+ } else if isBlurred {
177
+ onBlur()
178
+ }
179
+ if editing && !isBlurred {
180
+ isBlurred = true
181
+ }
182
+ }, onCommit: {})
183
+ .disabled(disabled)
184
+ .foregroundColor(disabled ? Colors.black08 : Colors.black17)
185
+ .font(.system(size: 15, weight: fontWeight))
186
+ .keyboardType(keyboardType)
187
+ }
188
+
189
+ if isFocused && !text.isEmpty {
190
+ SwiftButton(action: { text = "" }) {
191
+ Image(systemName: "xmark.circle.fill")
192
+ .foregroundColor(Colors.black12)
193
+ .frame(width: 16, height: 16)
194
+ }
195
+ .padding(.leading, Spacing.S)
196
+ }
197
+
198
+ RenderRightIconSearch(loading: loading, icon: icon, color: disabled ? Colors.black08 : iconColor, onClick: onRightIconPressed)
199
+ }
200
+ .padding(.horizontal, Spacing.M)
201
+ .padding(.vertical, Spacing.S)
202
+ .background(Colors.black01)
203
+ .cornerRadius(Radius.XL)
204
+ .overlay(
205
+ RoundedRectangle(cornerRadius: Radius.XL)
206
+ .stroke(getBorderColor(isFocused: isFocused, error: error, disabled: disabled), lineWidth: showBorder ? 1 : 0)
207
+ )
208
+ .frame(maxWidth: showButtonText ? .infinity : nil, alignment: .leading)
209
+
210
+ if showButtonText {
211
+ SwiftButton(action: onPressButtonText) {
212
+ Text(buttonText)
213
+ .foregroundColor(Colors.black17)
214
+ .font(.system(size: 14, weight: .medium))
215
+ }
216
+ .padding(.leading, Spacing.L)
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+
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
234
+ }
235
+ }
236
+
237
+
238
+
@@ -0,0 +1,242 @@
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
+