diff --git a/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift b/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift index 45ff928..8208d64 100644 --- a/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift +++ b/Sources/PressureEstimationsFeature/EquipmentMeasurementForm.swift @@ -19,7 +19,7 @@ public struct EquipmentMeasurementForm { @ObservableState public struct State: Equatable, Sendable { @Presents public var destination: Destination.State? - @Shared public var sharedSettings: SharedPressureEstimationSettings + @Shared public var sharedSettings: SharedPressureEstimationState public var allowEquipmentTypeSelection: Bool public var equipmentType: EquipmentMeasurement.EquipmentType public var focusedField: Field? @@ -28,7 +28,7 @@ public struct EquipmentMeasurementForm { public init( allowEquipmentTypeSelection: Bool = true, destination: Destination.State? = nil, - sharedSettings: Shared, + sharedSettings: Shared, equipmentType: EquipmentMeasurement.EquipmentType = .airHandler, focusedField: Field? = nil, measurements: Measurements = .init() @@ -224,8 +224,8 @@ public struct EquipmentMeasurementForm { } } -extension Store where State == EquipmentMeasurementForm.State { - +fileprivate extension Store where State == EquipmentMeasurementForm.State { + func prompt( field: EquipmentMeasurementForm.State.Field ) -> String { @@ -258,7 +258,7 @@ extension Store where State == EquipmentMeasurementForm.State { case .supplyPlenumPressure: return "Supply" case .airflow: - return "Airflow" + return "CFM" } } @@ -293,12 +293,7 @@ public struct EquipmentMeasurementFormView: View { } Section { Grid(alignment: .leading, horizontalSpacing: 40) { - ForEach(store.pressureFields) { field in - GridRow { - TextLabel(store.label(field: field)) - textField(for: field) - } - } + ForEach(store.pressureFields, content: gridRow(for:)) } } header: { HStack { @@ -310,10 +305,7 @@ public struct EquipmentMeasurementFormView: View { Section { Grid(alignment: .leading, horizontalSpacing: 60) { - GridRow { - TextLabel(store.label(field: .airflow)) - textField(for: .airflow) - } + gridRow(for: .airflow) } } footer: { HStack { @@ -325,6 +317,7 @@ public struct EquipmentMeasurementFormView: View { } } .bind($focusedField, to: $store.focusedField) + .labeledContentStyle(.gridRow) .onAppear { send(.onAppear) } .textLabelStyle(.boldSecondary) .textFieldStyle(.roundedBorder) @@ -349,7 +342,13 @@ public struct EquipmentMeasurementFormView: View { FlaggedMeasurementListView(store: store) } } - + + private func gridRow(for field: EquipmentMeasurementForm.State.Field) -> some View { + TextLabeledContent(store.label(field: field)) { + textField(for: field) + } + } + private func textField( for field: EquipmentMeasurementForm.State.Field ) -> some View { @@ -424,7 +423,7 @@ fileprivate extension InfoViewFeature.State { NavigationStack { EquipmentMeasurementFormView( store: Store(initialState: EquipmentMeasurementForm.State( - sharedSettings: Shared(SharedPressureEstimationSettings())) + sharedSettings: Shared(SharedPressureEstimationState())) ) { EquipmentMeasurementForm() } diff --git a/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift b/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift index b1aa1f0..b943e03 100644 --- a/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift +++ b/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift @@ -26,13 +26,13 @@ public struct EquipmentSettingsForm { public var includesFilterDrop: Bool public var equipmentType: EquipmentMeasurement.EquipmentType public var focusedField: Field? = nil - @Shared public var sharedSettings: SharedPressureEstimationSettings + @Shared public var sharedSettings: SharedPressureEstimationState public init( destination: Destination.State? = nil, includesFilterDrop: Bool = false, equipmentType: EquipmentMeasurement.EquipmentType = .airHandler, - sharedSettings: Shared + sharedSettings: Shared ) { self.destination = destination self.includesFilterDrop = includesFilterDrop @@ -46,12 +46,14 @@ public struct EquipmentSettingsForm { } // Note: These need to be in display order. - public enum Field: Hashable, CaseIterable, FocusableField { + public enum Field: Hashable, CaseIterable, FocusableField, Identifiable { case heatingCapacity case minimumStaticPressure case maximumStaticPressure case ratedStaticPressure case manufacturersIncludedFilterPressureDrop + + public var id: Self { self } } } @@ -183,37 +185,9 @@ public struct EquipmentSettingsFormView: View { Section { Grid(alignment: .leading, horizontalSpacing: 40) { - GridRow { - TextLabel("Minimum") - textField( - "Minimum Pressure", - value: $store.sharedSettings.ratedStaticPressures.minimum, - fractionLength: 2 - ) - .focused($focusedField, equals: .minimumStaticPressure) - .decimalPad() - } - GridRow { - TextLabel("Maximum") - textField( - "Maximum Pressure", - value: $store.sharedSettings.ratedStaticPressures.maximum, - fractionLength: 2 - ) - .focused($focusedField, equals: .maximumStaticPressure) - .decimalPad() - } - GridRow { - TextLabel("Rated") - textField( - "Rated Pressure", - value: $store.sharedSettings.ratedStaticPressures.rated, - fractionLength: 2 - ) - .focused($focusedField, equals: .ratedStaticPressure) - .decimalPad() - } + ForEach(RatingsField.allCases, content: ratingsRow(for:)) } + .labeledContentStyle(.gridRow) } header: { header("Rated Static Pressure", infoView: .ratedStaticPressures) } @@ -231,8 +205,7 @@ public struct EquipmentSettingsFormView: View { Spacer() textField( "Filter Drop", - value: $store.sharedSettings.manufacturersIncludedFilterPressureDrop, - fractionLength: 2 + value: $store.sharedSettings.manufacturersIncludedFilterPressureDrop ) .focused($focusedField, equals: .manufacturersIncludedFilterPressureDrop) .decimalPad() @@ -265,6 +238,27 @@ public struct EquipmentSettingsFormView: View { } } + private func ratingsRow(for ratingsField: RatingsField) -> some View { + + func binding(for ratingsField: RatingsField) -> Binding { + 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( infoView: EquipmentSettingsForm.InfoView, label: @escaping () -> Label @@ -283,20 +277,13 @@ public struct EquipmentSettingsFormView: View { header(infoView: infoView) { Text(title) } } - private func textField( - _ title: String, - value: Binding, - fractionLength: Int = 2 - ) -> some View { - TextField(title, value: value, fractionLength: fractionLength, prompt: Text(title)) - } - private func textField( _ title: String, value: Binding, fractionLength: Int = 2 ) -> some View { TextField(title, value: value, fractionLength: fractionLength, prompt: Text(title)) + .onSubmit { send(.submitField) } } } @@ -348,12 +335,71 @@ 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" } +} + +//fileprivate extension Store where State == EquipmentSettingsForm.State { +// +// +// func label(for ratingsField: RatingsField) -> String { +// self.label(for: ratingsField.field) +// } +// +// func prompt(for ratingsField: RatingsField) -> String { +// self.prompt(for: ratingsField.field) +// } +// +// func label(for field: EquipmentSettingsForm.State.Field) -> String { +// switch field { +// case .heatingCapacity: +// return "Heating" +// case .minimumStaticPressure: +// return "Minimum" +// case .maximumStaticPressure: +// return "Maximum" +// case .ratedStaticPressure: +// return "Rated" +// case .manufacturersIncludedFilterPressureDrop: +// return "Filter Drop" +// } +// } +// +// +// func prompt(for field: EquipmentSettingsForm.State.Field) -> String { +// switch field { +// case .heatingCapacity: +// return "Heating Capacity" +// case .minimumStaticPressure, .maximumStaticPressure, .ratedStaticPressure: +// return "\(label(for: field)) Pressure" +// case .manufacturersIncludedFilterPressureDrop: +// return label(for: field) +// } +// } +//} + #Preview { NavigationStack { EquipmentSettingsFormView( store: Store( initialState: EquipmentSettingsForm.State( - sharedSettings: Shared(SharedPressureEstimationSettings()) + sharedSettings: Shared(SharedPressureEstimationState()) ) ) { EquipmentSettingsForm()._printChanges() diff --git a/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift b/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift index 70ab8f4..31fd867 100644 --- a/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift +++ b/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift @@ -20,11 +20,11 @@ public struct FlaggedMeasurementsList: Sendable { public struct State: Equatable { @Presents public var destination: Destination.State? - @Shared var sharedSettings: SharedPressureEstimationSettings + @Shared var sharedSettings: SharedPressureEstimationState public init( destination: Destination.State? = nil, - sharedSettings: Shared + sharedSettings: Shared ) { self.destination = destination self._sharedSettings = sharedSettings @@ -36,7 +36,7 @@ public struct FlaggedMeasurementsList: Sendable { return [.coilDrop, .filterDrop] } - public subscript(dynamicMember keyPath: WritableKeyPath) -> T { + public subscript(dynamicMember keyPath: WritableKeyPath) -> T { get { sharedSettings[keyPath: keyPath] } set { sharedSettings[keyPath: keyPath] = newValue } } @@ -58,7 +58,7 @@ public struct FlaggedMeasurementsList: Sendable { public enum View { case addButtonTapped case destination(DestinationAction) - case editButtonTapped(id: SharedPressureEstimationSettings.FlaggedMeasurementContainer.ID) + case editButtonTapped(id: SharedPressureEstimationState.FlaggedMeasurementContainer.ID) case onAppear @CasePathable @@ -282,7 +282,7 @@ public struct FlaggedMeasurementListView: View { #if DEBUG private let budgets = BudgetedPercentEnvelope(equipmentType: .airHandler, fanType: .constantSpeed) -private let flaggedMeasurements = IdentifiedArrayOf( +private let flaggedMeasurements = IdentifiedArrayOf( uniqueElements: [ .init( id: UUID(0), @@ -303,7 +303,7 @@ private let flaggedMeasurements = IdentifiedArrayOf { +extension PersistenceReaderKey where Self == InMemoryKey { static var sharedPressureEstimationSettings: Self { .inMemory("sharedPressureEstimationSettings") } diff --git a/Sources/Styleguide/Styles/ButtonStyles.swift b/Sources/Styleguide/Styles/ButtonStyles.swift index 84f4e06..6c8c4cd 100644 --- a/Sources/Styleguide/Styles/ButtonStyles.swift +++ b/Sources/Styleguide/Styles/ButtonStyles.swift @@ -49,11 +49,7 @@ public struct DefaultInfoButtonStyle: PrimitiveButtonStyle { .foregroundStyle(color) .labelStyle(labelStyle) } -// configuration.label -// .font(font) -// .foregroundStyle(color.opacity(configuration.isPressed ? 0.5 : 1)) -// .labelStyle(labelStyle) -// .scaleEffect(configuration.isPressed ? 0.8 : 1) + .buttonStyle(.plain) } } diff --git a/Sources/Styleguide/Styles/TextLabeledContentStyle.swift b/Sources/Styleguide/Styles/TextLabeledContentStyle.swift new file mode 100644 index 0000000..acdaacb --- /dev/null +++ b/Sources/Styleguide/Styles/TextLabeledContentStyle.swift @@ -0,0 +1,17 @@ +import SwiftUI + +public struct GridRowTextLabeledContentStyle: LabeledContentStyle { + + public func makeBody(configuration: Configuration) -> some View { + GridRow { + configuration.label + configuration.content + } + } +} + +extension LabeledContentStyle where Self == GridRowTextLabeledContentStyle { + public static var gridRow: Self { + GridRowTextLabeledContentStyle() + } +} diff --git a/Sources/Styleguide/TextLabeledContent.swift b/Sources/Styleguide/TextLabeledContent.swift index 796392e..47fb1d0 100644 --- a/Sources/Styleguide/TextLabeledContent.swift +++ b/Sources/Styleguide/TextLabeledContent.swift @@ -49,6 +49,15 @@ extension TextLabeledContent where Label == Text { } #Preview { - TextLabeledContent("Label") { Text("Content") } + VStack { + TextLabeledContent("Label") { Text("Content") } + .textLabelStyle(.boldSecondary) + + Grid { + TextLabeledContent("One") { Text("One-Content") } + TextLabeledContent("Two") { Text("Two-Content") } + } .textLabelStyle(.boldSecondary) + .labeledContentStyle(.gridRow) + } }