feat: Breaks rated static pressure section into it's own view.
This commit is contained in:
@@ -27,6 +27,7 @@ public struct EquipmentSettingsForm {
|
|||||||
public var equipmentType: EquipmentMeasurement.EquipmentType
|
public var equipmentType: EquipmentMeasurement.EquipmentType
|
||||||
public var focusedField: Field? = nil
|
public var focusedField: Field? = nil
|
||||||
@Shared public var sharedSettings: SharedPressureEstimationState
|
@Shared public var sharedSettings: SharedPressureEstimationState
|
||||||
|
public var ratedStaticPressures: RatedStaticPressuresSection.State
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
destination: Destination.State? = nil,
|
destination: Destination.State? = nil,
|
||||||
@@ -38,21 +39,31 @@ public struct EquipmentSettingsForm {
|
|||||||
self.includesFilterDrop = includesFilterDrop
|
self.includesFilterDrop = includesFilterDrop
|
||||||
self.equipmentType = equipmentType
|
self.equipmentType = equipmentType
|
||||||
self._sharedSettings = sharedSettings
|
self._sharedSettings = sharedSettings
|
||||||
|
self.ratedStaticPressures = .init(staticPressures: sharedSettings.equipmentMetadata.ratedStaticPressures)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isValid: Bool {
|
public var isValid: Bool {
|
||||||
guard equipmentType == .furnaceAndCoil else { return true }
|
guard equipmentType == .furnaceAndCoil
|
||||||
return sharedSettings.heatingCapacity != nil
|
else { return ratedStaticPressures.isValid }
|
||||||
|
return ratedStaticPressures.isValid && sharedSettings.heatingCapacity != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: These need to be in display order.
|
// Note: These need to be in display order.
|
||||||
public enum Field: Hashable, CaseIterable, FocusableField, Identifiable {
|
public enum Field: Hashable, CaseIterable, FocusableField, Identifiable {
|
||||||
case heatingCapacity
|
case heatingCapacity
|
||||||
case minimumStaticPressure
|
case ratedStaticPressure(RatedStaticPressuresSection.State.FocusedField)
|
||||||
case maximumStaticPressure
|
|
||||||
case ratedStaticPressure
|
|
||||||
case manufacturersIncludedFilterPressureDrop
|
case manufacturersIncludedFilterPressureDrop
|
||||||
|
|
||||||
|
public static var allCases: [EquipmentSettingsForm.State.Field] {
|
||||||
|
[
|
||||||
|
.heatingCapacity,
|
||||||
|
.ratedStaticPressure(.maximum),
|
||||||
|
.ratedStaticPressure(.minimum),
|
||||||
|
.ratedStaticPressure(.rated),
|
||||||
|
.manufacturersIncludedFilterPressureDrop
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
public var id: Self { self }
|
public var id: Self { self }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,6 +71,7 @@ public struct EquipmentSettingsForm {
|
|||||||
public enum Action: BindableAction, ViewAction {
|
public enum Action: BindableAction, ViewAction {
|
||||||
case binding(BindingAction<State>)
|
case binding(BindingAction<State>)
|
||||||
case destination(PresentationAction<Destination.Action>)
|
case destination(PresentationAction<Destination.Action>)
|
||||||
|
case ratedStaticPressures(RatedStaticPressuresSection.Action)
|
||||||
case view(View)
|
case view(View)
|
||||||
|
|
||||||
@CasePathable
|
@CasePathable
|
||||||
@@ -72,6 +84,9 @@ public struct EquipmentSettingsForm {
|
|||||||
|
|
||||||
public var body: some Reducer<State, Action> {
|
public var body: some Reducer<State, Action> {
|
||||||
BindingReducer()
|
BindingReducer()
|
||||||
|
Scope(state: \.ratedStaticPressures, action: \.ratedStaticPressures) {
|
||||||
|
RatedStaticPressuresSection()
|
||||||
|
}
|
||||||
Reduce<State, Action> { state, action in
|
Reduce<State, Action> { state, action in
|
||||||
switch action {
|
switch action {
|
||||||
|
|
||||||
@@ -96,6 +111,14 @@ public struct EquipmentSettingsForm {
|
|||||||
case .destination:
|
case .destination:
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
|
|
||||||
|
case .ratedStaticPressures(.delegate(.infoButtonTapped)):
|
||||||
|
state.destination = .infoView(.init(view: .ratedStaticPressures))
|
||||||
|
return .none
|
||||||
|
|
||||||
|
case .ratedStaticPressures:
|
||||||
|
return .none
|
||||||
|
|
||||||
case let .view(action):
|
case let .view(action):
|
||||||
switch action {
|
switch action {
|
||||||
|
|
||||||
@@ -177,7 +200,6 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.dynamicBottomPadding()
|
.dynamicBottomPadding()
|
||||||
// .applyPadding()
|
|
||||||
} header: {
|
} header: {
|
||||||
if store.equipmentType == .airHandler {
|
if store.equipmentType == .airHandler {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
@@ -186,15 +208,9 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
RatedStaticPressuresSectionView(
|
||||||
Grid(alignment: .leading, horizontalSpacing: 40) {
|
store: store.scope(state: \.ratedStaticPressures, action: \.ratedStaticPressures)
|
||||||
ForEach(RatingsField.allCases, content: ratingsRow(for:))
|
)
|
||||||
}
|
|
||||||
.dynamicBottomPadding()
|
|
||||||
.labeledContentStyle(.gridRow)
|
|
||||||
} header: {
|
|
||||||
header("Rated Static Pressure", infoView: .ratedStaticPressures)
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@@ -240,27 +256,6 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ratingsRow(for ratingsField: RatingsField) -> some View {
|
|
||||||
|
|
||||||
func binding(for ratingsField: RatingsField) -> Binding<Double> {
|
|
||||||
switch ratingsField {
|
|
||||||
case .maximum:
|
|
||||||
return $store.sharedSettings.ratedStaticPressures.maximum
|
|
||||||
case .minimum:
|
|
||||||
return $store.sharedSettings.ratedStaticPressures.minimum
|
|
||||||
case .rated:
|
|
||||||
return $store.sharedSettings.ratedStaticPressures.rated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TextLabeledContent(ratingsField.label) {
|
|
||||||
TextField(ratingsField.prompt, value: binding(for: ratingsField), fractionLength: 2)
|
|
||||||
.decimalPad()
|
|
||||||
.focused($focusedField, equals: ratingsField.field)
|
|
||||||
.onSubmit { send(.submitField) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func header<Label: View>(
|
private func header<Label: View>(
|
||||||
infoView: EquipmentSettingsForm.InfoView,
|
infoView: EquipmentSettingsForm.InfoView,
|
||||||
label: @escaping () -> Label
|
label: @escaping () -> Label
|
||||||
@@ -289,17 +284,6 @@ public struct EquipmentSettingsFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate extension View {
|
|
||||||
@ViewBuilder
|
|
||||||
func applyPadding() -> some View {
|
|
||||||
#if os(macOS)
|
|
||||||
self.padding(.bottom, 20)
|
|
||||||
#else
|
|
||||||
self
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate struct BudgetFlagViewStyle: FlaggedViewStyle {
|
fileprivate struct BudgetFlagViewStyle: FlaggedViewStyle {
|
||||||
|
|
||||||
func makeBody(configuration: Configuration) -> some View {
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
@@ -348,26 +332,6 @@ fileprivate extension InfoViewFeature.State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate enum RatingsField: String, Hashable, CaseIterable, Identifiable {
|
|
||||||
case maximum
|
|
||||||
case minimum
|
|
||||||
case rated
|
|
||||||
|
|
||||||
var id: Self { self }
|
|
||||||
|
|
||||||
var field: EquipmentSettingsForm.State.Field {
|
|
||||||
switch self {
|
|
||||||
case .maximum: return .maximumStaticPressure
|
|
||||||
case .minimum: return .minimumStaticPressure
|
|
||||||
case .rated: return .ratedStaticPressure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var label: String { rawValue.capitalized }
|
|
||||||
|
|
||||||
var prompt: String { "\(label) Pressure" }
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
EquipmentSettingsFormView(
|
EquipmentSettingsFormView(
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import ComposableArchitecture
|
||||||
|
import SharedModels
|
||||||
|
import Styleguide
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
#warning("Add info view destination??")
|
||||||
|
/// 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
|
||||||
|
|
||||||
|
var label: String { rawValue.capitalized }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: Text("Max Static Pressure")
|
||||||
|
)
|
||||||
|
.decimalPad()
|
||||||
|
.focused($focusedField, equals: .maximum)
|
||||||
|
}
|
||||||
|
GridRow {
|
||||||
|
label(for: .minimum)
|
||||||
|
TextField(
|
||||||
|
"Minimum",
|
||||||
|
value: $store.minPressure,
|
||||||
|
fractionLength: 2,
|
||||||
|
prompt: Text("Min Static Pressure")
|
||||||
|
)
|
||||||
|
.decimalPad()
|
||||||
|
.focused($focusedField, equals: .minimum)
|
||||||
|
}
|
||||||
|
GridRow {
|
||||||
|
label(for: .rated)
|
||||||
|
TextField(
|
||||||
|
"Rated",
|
||||||
|
value: $store.ratedPressure,
|
||||||
|
fractionLength: 2,
|
||||||
|
prompt: Text("Rated Static Pressure")
|
||||||
|
)
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user