import ComposableArchitecture import SharedModels import Styleguide import SwiftUI @Reducer public struct EstimationForm { public init() { } @ObservableState public struct State: Equatable { public var cfmPerTon: Int public var coolingCapacity: CoolingCapacity public var filterPressureDrop: Double? public var name: String public init( cfmPerTon: Int = 350, coolingCapacity: CoolingCapacity = .default, filterPressureDrop: Double? = nil, name: String = "" ) { self.cfmPerTon = cfmPerTon self.filterPressureDrop = filterPressureDrop self.coolingCapacity = coolingCapacity self.name = name } public var airflow: Double { Double(cfmPerTon) * coolingCapacity.rawValue } public var isValid: Bool { !name.isEmpty } } public enum Action: BindableAction { case binding(BindingAction) } public var body: some Reducer { BindingReducer() Reduce { state, action in switch action { case .binding: return .none } } } } public struct EstimationFormView: View { @Bindable public var store: StoreOf public var body: some View { Form { Section("Estimation Name") { HStack { TextLabel("Name") .padding(.trailing, 40) TextField( "Name", text: $store.name, prompt: Text("Required") ) } } Section("Airflow") { Grid(alignment: .leading, horizontalSpacing: 40) { GridRow { HStack { TextLabel("Capacity") Spacer() CoolingCapacityPicker( selection: $store.coolingCapacity ) } .gridCellColumns(2) } GridRow { HStack { TextLabel("CFM / Ton") Spacer() TextField( "CFM / Ton", value: $store.cfmPerTon, format: .number, prompt: Text("CFM") ) .frame(width: 100) .numberPad() } .gridCellColumns(2) } } } Section("Filter Pressure Drop") { HStack { TextLabel("Pressure Drop") Spacer() TextField( "Filter Drop", value: $store.filterPressureDrop, fractionLength: 2, prompt: Text("Optional") ) .frame(width: 100) .decimalPad() } } } .labelsHidden() .textLabelStyle(.boldSecondary) .textFieldStyle(.roundedBorder) } } #Preview { EstimationFormView( store: Store(initialState: EstimationForm.State()) { EstimationForm() } ) }