Skip to content

Commit f6f2f65

Browse files
Merge pull request #18 from componentskit/textinput
TextInput Components
2 parents 3f8c7bb + 9cacefe commit f6f2f65

File tree

19 files changed

+855
-56
lines changed

19 files changed

+855
-56
lines changed

Examples/DemosApp/DemosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Examples/DemosApp/DemosApp/ComponentsPreview/Helpers/PreviewPickers.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import ComponentsKit
22
import SwiftUI
33

4+
// MARK: - AutocapitalizationPicker
5+
6+
struct AutocapitalizationPicker: View {
7+
@Binding var selection: TextAutocapitalization
8+
9+
var body: some View {
10+
Picker("Autocapitalization", selection: $selection) {
11+
Text("Never").tag(TextAutocapitalization.never)
12+
Text("Characters").tag(TextAutocapitalization.characters)
13+
Text("Words").tag(TextAutocapitalization.words)
14+
Text("Sentences").tag(TextAutocapitalization.sentences)
15+
}
16+
}
17+
}
18+
419
// MARK: - ComponentColorPicker
520

621
struct ComponentColorPicker: View {
@@ -78,6 +93,29 @@ struct FontPicker: View {
7893
}
7994
}
8095

96+
// MARK: - KeyboardTypePicker
97+
98+
struct KeyboardTypePicker: View {
99+
@Binding var selection: UIKeyboardType
100+
101+
var body: some View {
102+
Picker("Keyboard Type", selection: $selection) {
103+
Text("Default").tag(UIKeyboardType.default)
104+
Text("asciiCapable").tag(UIKeyboardType.asciiCapable)
105+
Text("numbersAndPunctuation").tag(UIKeyboardType.numbersAndPunctuation)
106+
Text("URL").tag(UIKeyboardType.URL)
107+
Text("numberPad").tag(UIKeyboardType.numberPad)
108+
Text("phonePad").tag(UIKeyboardType.phonePad)
109+
Text("namePhonePad").tag(UIKeyboardType.namePhonePad)
110+
Text("emailAddress").tag(UIKeyboardType.emailAddress)
111+
Text("decimalPad").tag(UIKeyboardType.decimalPad)
112+
Text("twitter").tag(UIKeyboardType.twitter)
113+
Text("webSearch").tag(UIKeyboardType.webSearch)
114+
Text("asciiCapableNumberPad").tag(UIKeyboardType.asciiCapableNumberPad)
115+
}
116+
}
117+
}
118+
81119
// MARK: - SizePicker
82120

83121
struct SizePicker: View {
@@ -92,6 +130,24 @@ struct SizePicker: View {
92130
}
93131
}
94132

133+
// MARK: - SubmitTypePicker
134+
135+
struct SubmitTypePicker: View {
136+
@Binding var selection: SubmitType
137+
138+
var body: some View {
139+
Picker("Submit Type", selection: $selection) {
140+
Text("done").tag(SubmitType.done)
141+
Text("go").tag(SubmitType.go)
142+
Text("join").tag(SubmitType.join)
143+
Text("route").tag(SubmitType.route)
144+
Text("return").tag(SubmitType.return)
145+
Text("next").tag(SubmitType.next)
146+
Text("continue").tag(SubmitType.continue)
147+
}
148+
}
149+
}
150+
95151
// MARK: - UniversalColorPicker
96152

97153
struct UniversalColorPicker: View {

Examples/DemosApp/DemosApp/ComponentsPreview/Helpers/PreviewWrapper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct PreviewWrapper<Content: View>: View {
1111
ZStack(alignment: Alignment(horizontal: .leading, vertical: .top)) {
1212
self.content()
1313
.padding(.all)
14-
.frame(height: 120)
14+
.frame(height: 150)
1515
.frame(maxWidth: .infinity)
1616
.overlay {
1717
RoundedRectangle(

Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/InputFieldPreview.swift

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,35 +29,15 @@ struct InputFieldPreview: View {
2929
)
3030
}
3131
Form {
32-
Picker("Autocapitalization", selection: self.$model.autocapitalization) {
33-
Text("Never").tag(InputFieldTextAutocapitalization.never)
34-
Text("Characters").tag(InputFieldTextAutocapitalization.characters)
35-
Text("Words").tag(InputFieldTextAutocapitalization.words)
36-
Text("Sentences").tag(InputFieldTextAutocapitalization.sentences)
37-
}
32+
AutocapitalizationPicker(selection: self.$model.autocapitalization)
3833
Toggle("Autocorrection Enabled", isOn: self.$model.isAutocorrectionEnabled)
3934
ComponentOptionalColorPicker(selection: self.$model.color)
40-
FontPicker(selection: self.$model.font)
41-
4235
CornerRadiusPicker(selection: self.$model.cornerRadius) {
4336
Text("Custom: 20px").tag(ComponentRadius.custom(20))
4437
}
4538
Toggle("Enabled", isOn: self.$model.isEnabled)
4639
FontPicker(selection: self.$model.font)
47-
Picker("Keyboard Type", selection: self.$model.keyboardType) {
48-
Text("Default").tag(UIKeyboardType.default)
49-
Text("asciiCapable").tag(UIKeyboardType.asciiCapable)
50-
Text("numbersAndPunctuation").tag(UIKeyboardType.numbersAndPunctuation)
51-
Text("URL").tag(UIKeyboardType.URL)
52-
Text("numberPad").tag(UIKeyboardType.numberPad)
53-
Text("phonePad").tag(UIKeyboardType.phonePad)
54-
Text("namePhonePad").tag(UIKeyboardType.namePhonePad)
55-
Text("emailAddress").tag(UIKeyboardType.emailAddress)
56-
Text("decimalPad").tag(UIKeyboardType.decimalPad)
57-
Text("twitter").tag(UIKeyboardType.twitter)
58-
Text("webSearch").tag(UIKeyboardType.webSearch)
59-
Text("asciiCapableNumberPad").tag(UIKeyboardType.asciiCapableNumberPad)
60-
}
40+
KeyboardTypePicker(selection: self.$model.keyboardType)
6141
Toggle("Placeholder", isOn: .init(
6242
get: {
6343
return self.model.placeholder != nil
@@ -69,15 +49,7 @@ struct InputFieldPreview: View {
6949
Toggle("Required", isOn: self.$model.isRequired)
7050
Toggle("Secure Input", isOn: self.$model.isSecureInput)
7151
SizePicker(selection: self.$model.size)
72-
Picker("Submit Type", selection: self.$model.submitType) {
73-
Text("done").tag(SubmitType.done)
74-
Text("go").tag(SubmitType.go)
75-
Text("join").tag(SubmitType.join)
76-
Text("route").tag(SubmitType.route)
77-
Text("return").tag(SubmitType.return)
78-
Text("next").tag(SubmitType.next)
79-
Text("continue").tag(SubmitType.continue)
80-
}
52+
SubmitTypePicker(selection: self.$model.submitType)
8153
UniversalColorPicker(
8254
title: "Tint Color",
8355
selection: self.$model.tintColor
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import ComponentsKit
2+
import Observation
3+
import SwiftUI
4+
import UIKit
5+
6+
struct TextInputPreviewPreview: View {
7+
@State private var model = TextInputVM {
8+
$0.placeholder = "Placeholder"
9+
$0.minRows = 1
10+
$0.maxRows = nil
11+
}
12+
13+
@State private var text: String = ""
14+
@FocusState private var isFocused: Bool
15+
16+
private let textInput = PreviewTextInput()
17+
18+
var body: some View {
19+
VStack {
20+
PreviewWrapper(title: "UIKit") {
21+
UKComponentPreview(model: self.model) {
22+
self.textInput
23+
}
24+
}
25+
PreviewWrapper(title: "SwiftUI") {
26+
SUTextInput(
27+
text: self.$text,
28+
isFocused: self.$isFocused,
29+
model: self.model
30+
)
31+
}
32+
Form {
33+
AutocapitalizationPicker(selection: self.$model.autocapitalization)
34+
Toggle("Autocorrection Enabled", isOn: self.$model.isAutocorrectionEnabled)
35+
ComponentOptionalColorPicker(selection: self.$model.color)
36+
CornerRadiusPicker(selection: self.$model.cornerRadius) {
37+
Text("Custom: 20px").tag(ComponentRadius.custom(20))
38+
}
39+
Toggle("Enabled", isOn: self.$model.isEnabled)
40+
FontPicker(selection: self.$model.font)
41+
KeyboardTypePicker(selection: self.$model.keyboardType)
42+
Picker("Max Rows", selection: self.$model.maxRows) {
43+
Text("2 Rows").tag(2)
44+
Text("3 Rows").tag(3)
45+
Text("No Limit").tag(Optional<Int>.none)
46+
}
47+
Picker("Min Rows", selection: self.$model.minRows) {
48+
Text("1 Row").tag(1)
49+
Text("2 Rows").tag(2)
50+
}
51+
Toggle("Placeholder", isOn: .init(
52+
get: {
53+
return self.model.placeholder != nil
54+
},
55+
set: { newValue in
56+
self.model.placeholder = newValue ? "Placeholder" : nil
57+
}
58+
))
59+
SizePicker(selection: self.$model.size)
60+
SubmitTypePicker(selection: self.$model.submitType)
61+
UniversalColorPicker(
62+
title: "Tint Color",
63+
selection: self.$model.tintColor
64+
)
65+
}
66+
}
67+
.toolbar {
68+
ToolbarItem(placement: .primaryAction) {
69+
if (self.textInput.isEditing || self.isFocused) && !ProcessInfo.processInfo.isiOSAppOnMac {
70+
Button("Hide Keyboard") {
71+
self.isFocused = false
72+
self.textInput.resignFirstResponder()
73+
}
74+
}
75+
}
76+
}
77+
}
78+
}
79+
80+
@Observable
81+
private final class PreviewTextInput: UKTextInput {
82+
var isEditing: Bool = false
83+
84+
func textViewDidBeginEditing(_ textView: UITextView) {
85+
self.isEditing = true
86+
}
87+
func textViewDidEndEditing(_ textView: UITextView) {
88+
self.isEditing = false
89+
}
90+
}
91+
92+
#Preview {
93+
TextInputPreviewPreview()
94+
}

Examples/DemosApp/DemosApp/Core/App.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ struct App: View {
2121
NavigationLinkWithTitle("Segmented Control") {
2222
SegmentedControlPreview()
2323
}
24+
NavigationLinkWithTitle("Text Field") {
25+
TextInputPreviewPreview()
26+
}
2427
}
2528

2629
Section("Login Demo") {

Examples/DemosApp/DemosApp/Demos/Login/SwiftUILogin.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ struct SwiftUILogin: View {
1010
case name
1111
case email
1212
case password
13+
case bio
1314
}
1415

1516
@State private var selectedPage = Pages.signIn
1617

1718
@State private var name = ""
1819
@State private var email = ""
1920
@State private var password = ""
21+
@State private var bio = ""
2022

2123
@FocusState private var focusedInput: Input?
2224
@State private var isConsented: Bool = false
@@ -97,6 +99,20 @@ struct SwiftUILogin: View {
9799
}
98100
)
99101

102+
if self.selectedPage == .signUp {
103+
SUTextInput(
104+
text: self.$bio,
105+
globalFocus: self.$focusedInput,
106+
localFocus: .bio,
107+
model: .init {
108+
$0.placeholder = "Tell about yourself"
109+
$0.minRows = 3
110+
$0.maxRows = 5
111+
$0.isEnabled = !self.isLoading
112+
}
113+
)
114+
}
115+
100116
SUCheckbox(
101117
isSelected: self.$isConsented,
102118
model: .init {
@@ -132,7 +148,8 @@ struct SwiftUILogin: View {
132148
}
133149
.frame(maxWidth: 500)
134150
.onChange(of: self.selectedPage) { _, newValue in
135-
if newValue == .signIn && self.focusedInput == .name {
151+
if newValue == .signIn,
152+
self.focusedInput == .name || self.focusedInput == .bio {
136153
self.focusedInput = .email
137154
}
138155
}

Examples/DemosApp/DemosApp/Demos/Login/UIKitLogin.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ final class UIKitLogin: UIViewController {
5858
$0.isSecureInput = true
5959
}
6060
)
61+
private let bioInput = UKTextInput(
62+
model: .init {
63+
$0.placeholder = "Tell about yourself"
64+
$0.minRows = 3
65+
$0.maxRows = 5
66+
}
67+
)
6168
private let consentCheckbox = UKCheckbox(
6269
model: .init {
6370
$0.title = "By continuing, you accept our Terms of Service and Privacy Policy"
@@ -104,16 +111,14 @@ final class UIKitLogin: UIViewController {
104111
self.stackView.addArrangedSubview(self.nameInput)
105112
self.stackView.addArrangedSubview(self.emailInput)
106113
self.stackView.addArrangedSubview(self.passwordInput)
114+
self.stackView.addArrangedSubview(self.bioInput)
107115
self.stackView.addArrangedSubview(self.consentCheckbox)
108116
self.stackView.addArrangedSubview(self.continueButton)
109117

110118
self.pageControl.onSelectionChange = { [weak self] selectedPage in
111119
guard let self else { return }
112120

113-
if selectedPage == .signIn && self.nameInput.isFirstResponder {
114-
self.emailInput.becomeFirstResponder()
115-
}
116-
121+
self.dismissKeyboard()
117122
self.update()
118123
}
119124
self.nameInput.onValueChange = { [weak self] _ in
@@ -148,6 +153,7 @@ final class UIKitLogin: UIViewController {
148153
self.nameInput.resignFirstResponder()
149154
self.emailInput.resignFirstResponder()
150155
self.passwordInput.resignFirstResponder()
156+
self.bioInput.resignFirstResponder()
151157
}
152158

153159
private func style() {
@@ -185,9 +191,11 @@ final class UIKitLogin: UIViewController {
185191
switch self.pageControl.selectedId {
186192
case .signIn:
187193
self.nameInput.isHidden = true
194+
self.bioInput.isHidden = true
188195
self.titleLabel.text = "Welcome back"
189196
case .signUp:
190197
self.nameInput.isHidden = false
198+
self.bioInput.isHidden = false
191199
self.titleLabel.text = "Create an account"
192200
}
193201

@@ -206,6 +214,9 @@ final class UIKitLogin: UIViewController {
206214
self.consentCheckbox.model.update {
207215
$0.isEnabled = !self.isLoading
208216
}
217+
self.bioInput.model.update {
218+
$0.isEnabled = !self.isLoading
219+
}
209220
self.continueButton.model.update { [weak self] in
210221
guard let self else { return }
211222
$0.isEnabled = self.isButtonEnabled
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import UIKit
2+
3+
extension UIEdgeInsets {
4+
/// Creates an instance of `UIEdgeInsets` with equal insets.
5+
init(inset: CGFloat) {
6+
self.init(top: inset, left: inset, bottom: inset, right: inset)
7+
}
8+
}

0 commit comments

Comments
 (0)