163 lines
4.3 KiB
Swift

//
// LoginForm.swift
// Login
//
// Created by Javier Cicchelli on 01/12/2022.
// Copyright © 2022 Röck+Cöde. All rights reserved.
//
import SwiftUI
struct LoginForm: View {
// MARK: States
@FocusState private var focusedField: Field?
// MARK: Bindings
@Binding var username: String
@Binding var password: String
@Binding var errorMessage: String?
// MARK: Properties
let onReturn: () -> Void
// MARK: Body
var body: some View {
VStack(
alignment: .leading,
spacing: 16
) {
TextField(
NSLocalizedString(
"login.text_field.username.placeholder",
bundle: .module,
comment: "The placeholder for the username text field."
),
text: $username
) { isBeginEditing in
guard isBeginEditing, errorMessage != nil else { return }
errorMessage = nil
}
.textContentType(.username)
.lineLimit(1)
.autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.default)
.focused($focusedField, equals: .username)
.onSubmit {
onUsernameReturnPressed()
}
Divider()
SecureField(
NSLocalizedString(
"login.text_field.password.placeholder",
bundle: .module,
comment: "The placeholder for the password text field."
),
text: $password
)
.textContentType(.password)
.lineLimit(1)
.autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.default)
.focused($focusedField, equals: .password)
.onSubmit {
onPasswordReturnPressed()
}
if let errorMessage {
Divider()
Text(
NSLocalizedString(
errorMessage,
bundle: .module,
comment: "The error message received from the backend."
)
)
.font(.body)
.foregroundColor(.red)
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
.background(Color.primary.colorInvert())
.cornerRadius(8)
.onAppear {
setClearButtonIfNeeded()
}
}
}
// MARK: - Helpers
private extension LoginForm {
func setClearButtonIfNeeded() {
let textFieldAppearance = UITextField.appearance()
guard textFieldAppearance.clearButtonMode != .whileEditing else { return }
textFieldAppearance.clearButtonMode = .whileEditing
}
func onUsernameReturnPressed() {
guard !username.isEmpty else { return }
if password.isEmpty {
focusedField = .password
} else {
onReturn()
}
}
func onPasswordReturnPressed() {
guard !password.isEmpty else { return }
if username.isEmpty {
focusedField = .username
} else {
onReturn()
}
}
}
// MARK: - Enumerations
private extension LoginForm {
enum Field: Hashable {
case username
case password
}
}
// MARK: - Previews
struct LoginForm_Previews: PreviewProvider {
static var previews: some View {
LoginForm(
username: .constant("Some username"),
password: .constant("Some Password"),
errorMessage: .constant(nil),
onReturn: {}
)
.previewDisplayName("Login form with no error message")
LoginForm(
username: .constant("Some username"),
password: .constant("Some Password"),
errorMessage: .constant("Some error goes in here..."),
onReturn: {}
)
.previewDisplayName("Login form with some error message")
}
}