feat: Adds text labeled content and style.

This commit is contained in:
2024-06-11 16:31:59 -04:00
parent c6c45ffa7e
commit da8a8638c7
8 changed files with 144 additions and 77 deletions

View File

@@ -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<SharedPressureEstimationSettings>,
sharedSettings: Shared<SharedPressureEstimationState>,
equipmentType: EquipmentMeasurement.EquipmentType = .airHandler,
focusedField: Field? = nil,
measurements: Measurements = .init()
@@ -224,7 +224,7 @@ public struct EquipmentMeasurementForm {
}
}
extension Store where State == EquipmentMeasurementForm.State {
fileprivate extension Store where State == EquipmentMeasurementForm.State {
func prompt(
field: EquipmentMeasurementForm.State.Field
@@ -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)
@@ -350,6 +343,12 @@ public struct EquipmentMeasurementFormView: View {
}
}
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()
}

View File

@@ -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<SharedPressureEstimationSettings>
sharedSettings: Shared<SharedPressureEstimationState>
) {
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<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>(
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<Double>,
fractionLength: Int = 2
) -> some View {
TextField(title, value: value, fractionLength: fractionLength, prompt: Text(title))
}
private func textField(
_ title: String,
value: Binding<Double?>,
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()

View File

@@ -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<SharedPressureEstimationSettings>
sharedSettings: Shared<SharedPressureEstimationState>
) {
self.destination = destination
self._sharedSettings = sharedSettings
@@ -36,7 +36,7 @@ public struct FlaggedMeasurementsList: Sendable {
return [.coilDrop, .filterDrop]
}
public subscript<T>(dynamicMember keyPath: WritableKeyPath<SharedPressureEstimationSettings, T>) -> T {
public subscript<T>(dynamicMember keyPath: WritableKeyPath<SharedPressureEstimationState, T>) -> 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<SharedPressureEstimationSettings.FlaggedMeasurementContainer>(
private let flaggedMeasurements = IdentifiedArrayOf<SharedPressureEstimationState.FlaggedMeasurementContainer>(
uniqueElements: [
.init(
id: UUID(0),
@@ -303,7 +303,7 @@ private let flaggedMeasurements = IdentifiedArrayOf<SharedPressureEstimationSett
store: Store(
initialState: FlaggedMeasurementsList.State(
sharedSettings: Shared(
SharedPressureEstimationSettings(
SharedPressureEstimationState(
budgets: budgets,
equipmentMeasurement: .mock(type: .airHandler),
flaggedEquipmentMeasurement: nil,

View File

@@ -19,12 +19,12 @@ public struct PressureEstimationsFeature {
@ObservableState
public struct State: Equatable {
@Presents public var destination: Destination.State?
@Shared(.sharedPressureEstimationSettings) var sharedSettings = SharedPressureEstimationSettings()
@Shared(.sharedPressureEstimationSettings) var sharedSettings = SharedPressureEstimationState()
public var equipmentSettings: EquipmentSettingsForm.State
public init(
destination: Destination.State? = nil,
sharedSettings: SharedPressureEstimationSettings = .init()
sharedSettings: SharedPressureEstimationState = .init()
) {
self.destination = destination
self._sharedSettings = Shared(sharedSettings)

View File

@@ -4,7 +4,7 @@ import SharedModels
/// Holds onto shared values for several of the views in this feature.
@dynamicMemberLookup
public struct SharedPressureEstimationSettings: Equatable, Sendable {
public struct SharedPressureEstimationState: Equatable, Sendable {
public var budgets: BudgetedPercentEnvelope?
public var equipmentMeasurement: EquipmentMeasurement?
public var equipmentMetadata: EquipmentMetadata
@@ -53,7 +53,7 @@ public struct SharedPressureEstimationSettings: Equatable, Sendable {
}
}
extension PersistenceReaderKey where Self == InMemoryKey<SharedPressureEstimationSettings> {
extension PersistenceReaderKey where Self == InMemoryKey<SharedPressureEstimationState> {
static var sharedPressureEstimationSettings: Self {
.inMemory("sharedPressureEstimationSettings")
}

View File

@@ -49,11 +49,7 @@ public struct DefaultInfoButtonStyle<Style: LabelStyle>: 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)
}
}

View File

@@ -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()
}
}

View File

@@ -49,6 +49,15 @@ extension TextLabeledContent where Label == Text {
}
#Preview {
VStack {
TextLabeledContent("Label") { Text("Content") }
.textLabelStyle(.boldSecondary)
Grid {
TextLabeledContent("One") { Text("One-Content") }
TextLabeledContent("Two") { Text("Two-Content") }
}
.textLabelStyle(.boldSecondary)
.labeledContentStyle(.gridRow)
}
}