146 lines
3.9 KiB
Swift
146 lines
3.9 KiB
Swift
import ComposableArchitecture
|
|
import SharedModels
|
|
import Styleguide
|
|
import SwiftUI
|
|
|
|
/// Allows for rated static pressure fields to be optional values, setting their corresponding shared state values to zero when they're nilled out.
|
|
///
|
|
@Reducer
|
|
public struct RatedStaticPressuresSection {
|
|
|
|
@ObservableState
|
|
public struct State: Equatable {
|
|
|
|
@Shared public var staticPressures: RatedStaticPressures
|
|
public var focusedField: FocusedField?
|
|
public var maxPressure: Double?
|
|
public var minPressure: Double?
|
|
public var ratedPressure: Double?
|
|
|
|
public init(
|
|
staticPressures: Shared<RatedStaticPressures>
|
|
) {
|
|
self._staticPressures = staticPressures
|
|
self.maxPressure = staticPressures.maximum.wrappedValue
|
|
self.minPressure = staticPressures.minimum.wrappedValue
|
|
self.ratedPressure = staticPressures.rated.wrappedValue
|
|
}
|
|
|
|
public var isValid: Bool {
|
|
maxPressure != nil
|
|
&& minPressure != nil
|
|
&& ratedPressure != nil
|
|
}
|
|
|
|
public enum FocusedField: String, Hashable, CaseIterable, FocusableField {
|
|
case maximum
|
|
case minimum
|
|
case rated
|
|
|
|
public var label: String { rawValue.capitalized }
|
|
public var prompt: String { "\(label) Pressure"}
|
|
}
|
|
}
|
|
|
|
public enum Action: BindableAction {
|
|
case binding(BindingAction<State>)
|
|
case delegate(DelegateAction)
|
|
|
|
public enum DelegateAction {
|
|
case infoButtonTapped
|
|
}
|
|
}
|
|
|
|
public var body: some Reducer<State, Action> {
|
|
BindingReducer()
|
|
Reduce<State, Action> { state, action in
|
|
switch action {
|
|
case .binding(\.maxPressure):
|
|
state.staticPressures.maximum = state.maxPressure ?? 0
|
|
return .none
|
|
|
|
case .binding(\.minPressure):
|
|
state.staticPressures.minimum = state.minPressure ?? 0
|
|
return .none
|
|
|
|
case .binding(\.ratedPressure):
|
|
state.staticPressures.rated = state.ratedPressure ?? 0
|
|
return .none
|
|
|
|
case .binding:
|
|
return .none
|
|
|
|
case .delegate:
|
|
return .none
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct RatedStaticPressuresSectionView: View {
|
|
|
|
@FocusState private var focusedField: RatedStaticPressuresSection.State.FocusedField?
|
|
@Bindable var store: StoreOf<RatedStaticPressuresSection>
|
|
|
|
public init(store: StoreOf<RatedStaticPressuresSection>) {
|
|
self.store = store
|
|
}
|
|
|
|
public var body: some View {
|
|
Section {
|
|
Grid(alignment: .leading, horizontalSpacing: 40) {
|
|
GridRow {
|
|
label(for: .maximum)
|
|
TextField(
|
|
"Maximum",
|
|
value: $store.maxPressure,
|
|
fractionLength: 2,
|
|
prompt: prompt(for: .maximum)
|
|
)
|
|
.decimalPad()
|
|
.focused($focusedField, equals: .maximum)
|
|
}
|
|
GridRow {
|
|
label(for: .minimum)
|
|
TextField(
|
|
"Minimum",
|
|
value: $store.minPressure,
|
|
fractionLength: 2,
|
|
prompt: prompt(for: .minimum)
|
|
)
|
|
.decimalPad()
|
|
.focused($focusedField, equals: .minimum)
|
|
}
|
|
GridRow {
|
|
label(for: .rated)
|
|
TextField(
|
|
"Rated",
|
|
value: $store.ratedPressure,
|
|
fractionLength: 2,
|
|
prompt: prompt(for: .rated)
|
|
)
|
|
.decimalPad()
|
|
.focused($focusedField, equals: .rated)
|
|
}
|
|
}
|
|
.dynamicBottomPadding()
|
|
|
|
} header: {
|
|
HStack {
|
|
SectionHeaderLabel("Rated Static Pressures")
|
|
Spacer()
|
|
InfoButton { store.send(.delegate(.infoButtonTapped)) }
|
|
}
|
|
}
|
|
.bind($focusedField, to: $store.focusedField)
|
|
}
|
|
|
|
private func label(for field: RatedStaticPressuresSection.State.FocusedField) -> some View {
|
|
TextLabel(field.label)
|
|
}
|
|
|
|
private func prompt(for field: RatedStaticPressuresSection.State.FocusedField) -> Text {
|
|
Text(field.prompt)
|
|
}
|
|
}
|