80 lines
2.8 KiB
Swift
80 lines
2.8 KiB
Swift
import SwiftUI
|
|
|
|
/// Input fields per `DESIGN (1).md` → Components → Input Fields: "1px solid
|
|
/// #E2E8F0 bottom-border only... Use `label-caps` for field labels placed
|
|
/// strictly above the input." Border thickens to 2px while focused — the
|
|
/// same "shift, don't lift" interaction rule as `ZenithButtonStyle`.
|
|
///
|
|
/// Use this instead of a bare `TextField` + `.textFieldStyle(.roundedBorder)`
|
|
/// everywhere in the app; `.roundedBorder` fights this design system
|
|
/// directly (it's rounded and iOS-chrome-grey, the opposite of "sharp,
|
|
/// monochrome, architectural").
|
|
struct ZenithTextField: View {
|
|
let label: String
|
|
@Binding var text: String
|
|
var placeholder: String = ""
|
|
var isSecure = false
|
|
var keyboardType: UIKeyboardType = .default
|
|
var textContentType: UITextContentType?
|
|
var autocapitalization: TextInputAutocapitalization = .sentences
|
|
var autocorrectionDisabled = false
|
|
|
|
@FocusState private var isFocused: Bool
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: ZenithSpacing.unit) {
|
|
Text(label).zenithLabelCapsStyle()
|
|
|
|
Group {
|
|
if isSecure {
|
|
SecureField(placeholder, text: $text)
|
|
} else {
|
|
TextField(placeholder, text: $text)
|
|
}
|
|
}
|
|
.font(.zenithBodyMd)
|
|
.foregroundStyle(.zenithText)
|
|
.keyboardType(keyboardType)
|
|
.textContentType(textContentType)
|
|
.textInputAutocapitalization(autocapitalization)
|
|
.autocorrectionDisabled(autocorrectionDisabled)
|
|
.focused($isFocused)
|
|
.padding(.vertical, ZenithSpacing.unit * 2)
|
|
.overlay(alignment: .bottom) {
|
|
Rectangle()
|
|
.fill(isFocused ? Color.zenithBlack : Color.zenithBorder)
|
|
.frame(height: isFocused ? 2 : 1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A numeric variant bound to an optional `Double`, for amount fields — free
|
|
/// text with comma-or-dot decimal entry (matches how receipts are actually
|
|
/// typed in German usage) rather than a strict `TextField(value:format:)`
|
|
/// that rejects a comma outright.
|
|
struct ZenithAmountField: View {
|
|
let label: String
|
|
@Binding var value: Double?
|
|
var placeholder: String = "0,00"
|
|
|
|
private var textProxy: Binding<String> {
|
|
Binding(
|
|
get: { value.map { String(format: "%.2f", $0) } ?? "" },
|
|
set: { newValue in
|
|
let normalized = newValue.replacingOccurrences(of: ",", with: ".")
|
|
value = Double(normalized)
|
|
}
|
|
)
|
|
}
|
|
|
|
var body: some View {
|
|
ZenithTextField(
|
|
label: label,
|
|
text: textProxy,
|
|
placeholder: placeholder,
|
|
keyboardType: .decimalPad
|
|
)
|
|
}
|
|
}
|