Skip to content

Commit

Permalink
Create Textarea component and update documentation for both text inpu…
Browse files Browse the repository at this point in the history
…t components
  • Loading branch information
PavelHolec committed Nov 23, 2023
1 parent 9b380f7 commit 02a0f77
Show file tree
Hide file tree
Showing 18 changed files with 851 additions and 134 deletions.
58 changes: 34 additions & 24 deletions Sources/Orbit/Components/InputField.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
import SwiftUI
import UIKit

/// Also known as textbox. Offers users a simple input for a form.
/// An Orbit component that offers a simple text input. The component is an Orbit equivalent of the native `TextField` component.
///
/// When you have additional information or helpful examples, include prompt text to help users along.
/// The InputField is created with an optional label and a binding to a string value.
///
/// ```swift
/// InputField("Label", value: $email, prompt: "E-mail")
/// .keyboardType(.emailAddress)
/// .returnKeyType(.done)
/// .focused($isEmailFocused)
/// .inputFieldReturnAction {
/// // Action after the value is submitted
/// }
/// ```
///
/// For secure version, the `passwordStrength` hint can be provided.
///
/// ```swift
/// InputField(
/// "Password",
/// value: $password,
/// isSecure: true,
/// passwordStrength: passwordStrength,
/// message: .help("Must be at least 8 characters long")
/// )
/// ```
///
/// The component uses a custom ``TextField`` component (implemented using `UITextField`) that represents the native `TextField`.

/// ### Layout
///
/// The custom Orbit version of ``TextField`` component is used internally.
/// The component expands horizontally.
///
/// - Note: [Orbit definition](https://orbit.kiwi/components/inputfield/)
/// - Important: Component expands horizontally unless prevented by `fixedSize` modifier.
public struct InputField<Prefix: View, Suffix: View>: View, TextFieldBuildable {

@Environment(\.inputFieldBeginEditingAction) private var inputFieldBeginEditingAction
Expand Down Expand Up @@ -50,8 +75,7 @@ public struct InputField<Prefix: View, Suffix: View>: View, TextFieldBuildable {
state: state,
label: compactLabel,
message: message,
isFocused: isFocused,
isPlaceholder: value.isEmpty
isFocused: isFocused
) {
textField
} prefix: {
Expand Down Expand Up @@ -131,18 +155,11 @@ public struct InputField<Prefix: View, Suffix: View>: View, TextFieldBuildable {

public extension InputField {

/// Creates Orbit InputField component.
///
/// The keyboard related modifiers can be used directly on this component to modify the keyboard behaviour:
/// - `autocapitalization()`
/// - `autocorrectionDisabled()`
/// - `keyboardType()`
/// - `textContentType()`
/// Creates Orbit ``InputField`` component.
///
/// - Parameters:
/// - message: Optional message below the InputField.
/// - message: Optional message below the text field.
/// - messageHeight: Binding to the current height of the optional message.
/// - suffixAction: Optional action when suffix icon is tapped.
init(
_ label: String = "",
value: Binding<String>,
Expand Down Expand Up @@ -173,18 +190,11 @@ public extension InputField {
}
}

/// Creates Orbit InputField component with custom prefix or suffix.
///
/// The keyboard related modifiers can be used directly on this component to modify the keyboard behaviour:
/// - `autocapitalization()`
/// - `autocorrectionDisabled()`
/// - `keyboardType()`
/// - `textContentType()`
/// Creates Orbit ``InputField`` component with custom prefix or suffix.
///
/// - Parameters:
/// - message: Optional message below the InputField.
/// - message: Optional message below the text field.
/// - messageHeight: Binding to the current height of the optional message.
/// - suffixAction: Optional action when suffix icon is tapped.
init(
_ label: String = "",
value: Binding<String>,
Expand Down
3 changes: 1 addition & 2 deletions Sources/Orbit/Components/Select.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ public struct Select<Prefix: View, Suffix: View>: View {
InputContentButtonStyle(
state: state,
label: compactLabel,
message: message,
isPlaceholder: value == nil
message: message
) {
prefix
.accessibility(.selectPrefix)
Expand Down
167 changes: 167 additions & 0 deletions Sources/Orbit/Components/Textarea.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import SwiftUI
import UIKit

/// An Orbit component that offers multiline text input. The component is an Orbit equivalent of the native `TextField` component with vertical axis specified.
///
/// The Textarea is created with an optional label and a binding to a string value.
///
/// ```swift
/// Textarea("Label", value: $text, prompt: "Description")
/// .focused($isDescriptionFocused)
/// .inputFieldReturnAction {
/// // Action after the value is submitted
/// }
/// .frame(height: 200)
/// ```
///
/// The component uses a custom ``TextView`` component (implemented using `UITextView`) that represents the native `TextField`.
///
/// ### Layout
///
/// The component expands horizontally and vertically, unless limited by a `frame` modifier.
///
/// - Note: [Orbit definition](https://orbit.kiwi/components/input/textarea/)
public struct Textarea: View, TextFieldBuildable {

@Environment(\.inputFieldBeginEditingAction) private var inputFieldBeginEditingAction
@Environment(\.inputFieldEndEditingAction) private var inputFieldEndEditingAction
@Environment(\.isEnabled) private var isEnabled
@Environment(\.sizeCategory) private var sizeCategory

@State private var isFocused: Bool = false

private let label: String
@Binding private var value: String
private let prompt: String
private let state: InputState

private let message: Message?
@Binding private var messageHeight: CGFloat

// Builder properties (keyboard related)
var autocapitalizationType: UITextAutocapitalizationType = .none
var isAutocorrectionDisabled: Bool? = false
var keyboardType: UIKeyboardType = .default
var returnKeyType: UIReturnKeyType = .default
var textContentType: UITextContentType?
var shouldDeleteBackwardAction: (String) -> Bool = { _ in true }

public var body: some View {
FieldWrapper(
label,
message: message,
messageHeight: $messageHeight
) {
InputContent(
state: state,
message: message,
isFocused: isFocused
) {
textView
}
}
}

@ViewBuilder var textView: some View {
TextView(
value: $value,
prompt: prompt,
insets: .init(
top: .small,
left: .small,
bottom: .small,
right: .small
)
)
.returnKeyType(returnKeyType)
.autocorrectionDisabled(isAutocorrectionDisabled)
.keyboardType(keyboardType)
.textContentType(textContentType)
.autocapitalization(autocapitalizationType)
.shouldDeleteBackwardAction(shouldDeleteBackwardAction)
.accessibility(label: .init(label))
.inputFieldBeginEditingAction {
isFocused = true
inputFieldBeginEditingAction()
}
.inputFieldEndEditingAction {
isFocused = false
inputFieldEndEditingAction()
}
}
}

// MARK: - Inits
public extension Textarea {

/// Creates Orbit ``Textarea`` component.
///
/// - Parameters:
/// - message: Optional message below the text field.
/// - messageHeight: Binding to the current height of the optional message.
init(
_ label: String = "",
value: Binding<String>,
prompt: String = "",
state: InputState = .default,
message: Message? = nil,
messageHeight: Binding<CGFloat> = .constant(0)
) {
self.label = label
self._value = value
self.prompt = prompt
self.state = state
self.message = message
self._messageHeight = messageHeight
}
}

// MARK: - Previews
struct TextareaPreviews: PreviewProvider {

static var previews: some View {
PreviewWrapper {
VStack(alignment: .leading, spacing: .medium) {
StateWrapper("Enter \(String(repeating: "values ", count: 20))") { $value in
Textarea(
"Label",
value: $value,
prompt: "Enter \(String(repeating: "values ", count: 20))"
)
.frame(height: 100)
}

StateWrapper("") { $value in
Textarea(
"Label",
value: $value,
prompt: "Enter \(String(repeating: "values ", count: 20))"
)
.frame(height: 100)
}

HStack(alignment: .top, spacing: .medium) {
StateWrapper("This is my value") { $value in
Textarea(
"Label",
value: $value,
prompt: "Enter value"
)
.frame(height: 200)
}

StateWrapper("This is my value") { $value in
Textarea(
"Label",
value: $value,
prompt: "Enter value"
)
.frame(height: 200)
}
}
}
}
.padding(.medium)
.previewLayout(.sizeThatFits)
}
}
19 changes: 17 additions & 2 deletions Sources/Orbit/Orbit.docc/Components/Extensions/InputField.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

## Topics

### UITextField delegation
### Input and event modifiers

- ``disabled(_:)``
- ``inputFieldBeginEditingAction(_:)-2n7iu``
- ``inputFieldBeginEditingAction(_:)-63rdh``
- ``inputFieldEndEditingAction(_:)-1p8ux``
- ``inputFieldEndEditingAction(_:)-3vtzf``
- ``inputFieldFocus(_:)``
- ``inputFieldReturnAction(_:)-4p3ne``
- ``inputFieldReturnAction(_:)-8ukhl``
- ``inputFieldShouldReturnAction(_:)-82nse``
Expand All @@ -20,11 +22,24 @@
- ``inputFieldShouldChangeCharactersAction(_:)-7xzq2``
- ``InputFieldShouldChangeResult``

### UITextField components
### Managing text entry

- ``autocapitalization(_:)-6ecdh``
- ``autocorrectionDisabled(_:)-7sz54``
- ``keyboardType(_:)-1uu7i``
- ``returnKeyType(_:)``
- ``shouldDeleteBackwardAction(_:)``
- ``textContentType(_:)-4ng7o``

### UIKit components

- ``TextField``
- ``InsetableTextField``

### Customizing Appearance

- ``textColor(_:)``
- ``textFontWeight(_:)``
- ``textSize(_:)``
- ``textSize(custom:)``
- ``InputLabelStyle``
45 changes: 45 additions & 0 deletions Sources/Orbit/Orbit.docc/Components/Extensions/Textarea.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# ``Orbit/Textarea``

@Metadata {
@DocumentationExtension(mergeBehavior: append)
}

## Topics

### Input and event modifiers

- ``disabled(_:)``
- ``inputFieldBeginEditingAction(_:)-6qvc9``
- ``inputFieldBeginEditingAction(_:)-4935w``
- ``inputFieldEndEditingAction(_:)-238ir``
- ``inputFieldEndEditingAction(_:)-38zwl``
- ``inputFieldFocus(_:)``
- ``inputFieldReturnAction(_:)-2dxhj``
- ``inputFieldReturnAction(_:)-4sxsc``
- ``inputFieldShouldReturnAction(_:)-7ydww``
- ``inputFieldShouldReturnAction(_:)-8trm2``
- ``inputFieldShouldChangeCharactersAction(_:)-6g5c3``
- ``inputFieldShouldChangeCharactersAction(_:)-50iz2``
- ``InputFieldShouldChangeResult``

### Managing text entry

- ``autocapitalization(_:)-9k9yh``
- ``autocorrectionDisabled(_:)-4x1d6``
- ``keyboardType(_:)-7khza``
- ``returnKeyType(_:)``
- ``shouldDeleteBackwardAction(_:)``
- ``textContentType(_:)-6etk6``

### UIKit components

- ``TextView``
- ``InsetableTextView``

### Customizing Appearance

- ``textColor(_:)``
- ``textFontWeight(_:)``
- ``textSize(_:)``
- ``textSize(custom:)``
- ``InputLabelStyle``
Loading

0 comments on commit 02a0f77

Please sign in to comment.