From aa40985486228b3dae856888fc4cd7f08b50cf34 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 4 Jun 2024 15:16:12 -0400 Subject: [PATCH] Feat: Working on pressure estimations feature --- Package.swift | 3 +- .../EstimateSettingsForm.swift | 105 +++++++++++++++--- Sources/Styleguide/Buttons.swift | 6 +- Sources/Styleguide/InfoView.swift | 90 +++++++++------ Sources/Styleguide/Styles/ButtonStyles.swift | 1 + 5 files changed, 153 insertions(+), 52 deletions(-) diff --git a/Package.swift b/Package.swift index 5d7060e..8f3faf4 100644 --- a/Package.swift +++ b/Package.swift @@ -59,7 +59,8 @@ let package = Package( .target( name: "Styleguide", dependencies: [ - "SharedModels" + "SharedModels", + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), ] ), .testTarget( diff --git a/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift b/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift index 15df16a..1f274ba 100644 --- a/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift +++ b/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift @@ -6,22 +6,39 @@ import SwiftUI @Reducer public struct EstimateSettingsForm { + @CasePathable + public enum InfoView { + case budgets + case updatedAirflow + } + + @Reducer(state: .equatable) + public enum Destination { + case infoView(InfoViewFeature) + } + @ObservableState public struct State: Equatable { + @Presents public var destination: Destination.State? public var budgets: BudgetedPercentEnvelope - public let equipmentType: EquipmentType + public let equipmentMeasurement: EquipmentMeasurement public var fanType: FanType public var focusedField: Field? = nil public var updatedAirflow: Double? public init( - equipmentType: EquipmentType, + destination: Destination.State? = nil, + equipmentMeasurement: EquipmentMeasurement, fanType: FanType = .constantSpeed, updatedAirflow: Double? = nil ) { - self.budgets = .init(equipmentType: equipmentType, fanType: fanType) - self.equipmentType = equipmentType + self.destination = destination + self.equipmentMeasurement = equipmentMeasurement + self.budgets = .init( + equipmentType: equipmentMeasurement.equipmentType, + fanType: fanType + ) self.fanType = fanType self.updatedAirflow = updatedAirflow } @@ -42,19 +59,15 @@ public struct EstimateSettingsForm { public enum Action: BindableAction, ViewAction { case binding(BindingAction) + case destination(PresentationAction) case view(View) @CasePathable public enum View { - case infoButtonTapped(InfoButton) + case infoButtonTapped(InfoView) + case nextButtonTapped case resetButtonTapped case submitField - - @CasePathable - public enum InfoButton { - case budgets - case updatedAirflow - } } } @@ -62,9 +75,10 @@ public struct EstimateSettingsForm { BindingReducer() Reduce { state, action in switch action { + case .binding(\.fanType): state.budgets = .init( - equipmentType: state.equipmentType, + equipmentType: state.equipmentMeasurement.equipmentType, fanType: state.fanType ) return .none @@ -72,15 +86,29 @@ public struct EstimateSettingsForm { case .binding: return .none + case .destination(.dismiss): + state.destination = nil + return .none + + case .destination: + return .none + case let .view(action): switch action { - case .infoButtonTapped: + case let .infoButtonTapped(infoView): + state.destination = .infoView(.init(view: infoView)) + return .none + + case .nextButtonTapped: #warning("Fix me.") return .none case .resetButtonTapped: - state.budgets = .init(equipmentType: state.equipmentType, fanType: state.fanType) + state.budgets = .init( + equipmentType: state.equipmentMeasurement.equipmentType, + fanType: state.fanType + ) state.updatedAirflow = nil return .none @@ -91,6 +119,7 @@ public struct EstimateSettingsForm { } } } + .ifLet(\.$destination, action: \.destination) } } @@ -121,7 +150,7 @@ public struct EstimateSettingsFormView: View { Section { Grid(alignment: .leading, horizontalSpacing: 40) { - if store.equipmentType == .furnaceAndCoil { + if store.equipmentMeasurement.equipmentType == .furnaceAndCoil { GridRow { TextLabel("Coil") percentField("Coil Budget", value: $store.budgets.coilBudget.fraction) @@ -177,7 +206,11 @@ public struct EstimateSettingsFormView: View { } Section { - + TextField( + "Airflow", + value: $store.updatedAirflow, + fractionLength: 0 + ) } header: { HStack { Text("Updated Airflow") @@ -189,8 +222,9 @@ public struct EstimateSettingsFormView: View { ResetButton { send(.resetButtonTapped) } Spacer() Button("Next") { - #warning("Fix me.") + send(.nextButtonTapped) } + .disabled(!store.isValid) } .padding(.top) } @@ -198,6 +232,16 @@ public struct EstimateSettingsFormView: View { .labelsHidden() .bind($focusedField, to: $store.focusedField) .navigationTitle("Estimate Settings") + .sheet( + item: $store.scope( + state: \.destination?.infoView, + action: \.destination.infoView + ) + ) { store in + NavigationStack { + InfoView(store: store) + } + } } private func percentField( @@ -219,11 +263,36 @@ fileprivate struct BudgetFlagViewStyle: FlaggedViewStyle { } } +fileprivate extension InfoViewFeature.State { + init(view: EstimateSettingsForm.InfoView) { + switch view { + case .budgets: + self.init( + title: "Budgets", + body: """ + Budgeted percentages for static pressure estimations, these generally are set to + reasonable defaults, however you can change them if desired. + + Note: These must total up to 100%. + """ + ) + case .updatedAirflow: + self.init( + title: "Updated Airflow", + body: """ + This is used to generated estimated static pressures at the updated airflow + compared to the existing airflow of the system. + """ + ) + } + } +} + #Preview { NavigationStack { EstimateSettingsFormView( store: Store( - initialState: EstimateSettingsForm.State(equipmentType: .furnaceAndCoil) + initialState: EstimateSettingsForm.State(equipmentMeasurement: .furnaceAndCoil(.init())) ) { EstimateSettingsForm() } diff --git a/Sources/Styleguide/Buttons.swift b/Sources/Styleguide/Buttons.swift index d7f58d5..c8a48e2 100644 --- a/Sources/Styleguide/Buttons.swift +++ b/Sources/Styleguide/Buttons.swift @@ -14,7 +14,11 @@ public struct InfoButton: View { Button(action: action) { Label("Info", systemImage: "info.circle") } - .buttonStyle(infoButtonStyle) + .buttonStyle(.plain) + .labelStyle(.iconOnly) + .font(.title2) + .foregroundStyle(Color.accentColor) +// .buttonStyle(infoButtonStyle) } } diff --git a/Sources/Styleguide/InfoView.swift b/Sources/Styleguide/InfoView.swift index 3e2865d..7c36090 100644 --- a/Sources/Styleguide/InfoView.swift +++ b/Sources/Styleguide/InfoView.swift @@ -1,48 +1,74 @@ +import ComposableArchitecture import SwiftUI +@Reducer +public struct InfoViewFeature { + + public init() { } + + @ObservableState + public struct State: Equatable { + let titleText: String + let bodyText: String + + public init( + title titleText: String, + body bodyText: String + ) { + self.titleText = titleText + self.bodyText = bodyText + } + } + + public enum Action { + case dismissButtonTapped + } + + @Dependency(\.dismiss) var dismiss + + public var body: some ReducerOf { + Reduce { _, action in + switch action { + case .dismissButtonTapped: + return .run { _ in await self.dismiss() } + } + } + } +} + public struct InfoView: View { - let headingText: String - let bodyText: String + let store: StoreOf - - public init(heading headingText: String, body bodyText: String) { - self.headingText = headingText - self.bodyText = bodyText + public init(store: StoreOf) { + self.store = store } public var body: some View { VStack(spacing: 40) { - - ZStack { - Text(headingText) - .font(.headline) - .bold() - .foregroundStyle(Color.white) - .padding() - } - .background { - RoundedRectangle(cornerRadius: 10) - .foregroundStyle(Color.orange.opacity(0.6)) - } - - - ZStack { - Text(bodyText) - .font(.callout) - .padding() - } - .background { - RoundedRectangle(cornerRadius: 10) - .foregroundStyle(Color.secondary.opacity(0.3)) - } - .padding(.horizontal, 10) - + Text(store.bodyText) + .font(.callout) + .padding() Spacer() } + .navigationTitle(store.titleText) .padding(.horizontal) + .navigationBarBackButtonHidden() + .toolbar { + Button("Done") { + store.send(.dismissButtonTapped) + } + } } } #Preview { - InfoView(heading: "Generic Info View", body: "Explain what this view does here...") + InfoView( + store: Store( + initialState: InfoViewFeature.State( + title: "Generic Info View", + body: "Explain what this view does here..." + ), + reducer: InfoViewFeature.init + ) + ) } diff --git a/Sources/Styleguide/Styles/ButtonStyles.swift b/Sources/Styleguide/Styles/ButtonStyles.swift index 54a7d54..85d300f 100644 --- a/Sources/Styleguide/Styles/ButtonStyles.swift +++ b/Sources/Styleguide/Styles/ButtonStyles.swift @@ -22,6 +22,7 @@ public struct DefaultInfoButtonStyle: PrimitiveButtonStyle { public func makeBody(configuration: Configuration) -> some View { configuration.label .buttonStyle(.plain) + .font(.title2) .foregroundStyle(Color.accentColor) .labelStyle(.iconOnly) }