#if canImport(SwiftUI) import SwiftUI public struct FormFieldStyleConfiguration { fileprivate init( content: AnyView, validation: FieldValidationState? ) { self.content = content self.validation = validation } public let content: AnyView public let validation: FieldValidationState? } public protocol FormFieldStyle: Sendable { associatedtype Body: View @MainActor @ViewBuilder func makeBody(configuration: FormFieldStyleConfiguration) -> Body } public struct AutomaticFormFieldStyle: FormFieldStyle { public init() {} public func makeBody(configuration: FormFieldStyleConfiguration) -> some View { VStack(alignment: .leading) { configuration.content FormFieldValidationStatus(validation: configuration.validation) } } } extension FormFieldStyle where Self == AutomaticFormFieldStyle { public static var automatic: Self { Self() } } public struct FormFieldValidationStatus: View { public init(validation: FieldValidationState?) { self.validation = validation } let validation: FieldValidationState? public var body: some View { if self.validation?.isValidating == true { HStack(spacing: 6) { ProgressView() #if !os(Android) .controlSize(.small) #endif Text("Checking") .font(.footnote) .foregroundStyle(.secondary) } } ForEach(Array((self.validation?.errors ?? []).enumerated()), id: \.offset) { _, error in Text(error.message) .font(.footnote) .foregroundStyle(.red) } } } extension View { public func formFieldStyle(_ style: Style) -> some View { self.environment(\.formFieldStyle, AnyFormFieldStyle(style)) } } struct FormFieldContent: View { @Environment(\.formFieldStyle) private var formFieldStyle let fieldID: AnyHashable let keyPath: WritableKeyPath let submit: (AnyHashable) -> Void let validation: FieldValidationState? @Binding var value: Root let content: @MainActor (Binding) -> Content var body: some View { self.formFieldStyle.makeBody( FormFieldStyleConfiguration( content: AnyView( self.content(self.$value[dynamicMember: self.keyPath]) .onSubmit { self.submit(self.fieldID) } ), validation: self.validation ) ) } } private struct AnyFormFieldStyle { init(_ style: Style) { self.makeBody = { configuration in AnyView(style.makeBody(configuration: configuration)) } } var makeBody: @MainActor (FormFieldStyleConfiguration) -> AnyView } private enum FormFieldStyleKey: EnvironmentKey { static let defaultValue = AnyFormFieldStyle(AutomaticFormFieldStyle()) } private extension EnvironmentValues { var formFieldStyle: AnyFormFieldStyle { get { self[FormFieldStyleKey.self] } set { self[FormFieldStyleKey.self] = newValue } } } #endif