diff --git a/Package.swift b/Package.swift index 8f3faf4..b7fbc25 100644 --- a/Package.swift +++ b/Package.swift @@ -13,6 +13,7 @@ let package = Package( products: [ .library(name: "CalculateAtFeature", targets: ["CalculateAtFeature"]), .library(name: "EstimatedPressureDependency", targets: ["EstimatedPressureDependency"]), + .library(name: "FlaggedViews", targets: ["FlaggedViews"]), .library(name: "PressureEstimationsFeature", targets: ["PressureEstimationsFeature"]), .library(name: "SharedModels", targets: ["SharedModels"]), .library(name: "Styleguide", targets: ["Styleguide"]), @@ -55,6 +56,13 @@ let package = Package( .product(name: "DependenciesMacros", package: "swift-dependencies") ] ), + .target( + name: "FlaggedViews", + dependencies: [ + "SharedModels", + "Styleguide" + ] + ), .target(name: "SharedModels"), .target( name: "Styleguide", @@ -74,6 +82,7 @@ let package = Package( name: "PressureEstimationsFeature", dependencies: [ "EstimatedPressureDependency", + "FlaggedViews", "SharedModels", "Styleguide", .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), diff --git a/Sources/Styleguide/FlaggedEquipmentMeasurementView.swift b/Sources/FlaggedViews/FlaggedEquipmentMeasurementView.swift similarity index 100% rename from Sources/Styleguide/FlaggedEquipmentMeasurementView.swift rename to Sources/FlaggedViews/FlaggedEquipmentMeasurementView.swift diff --git a/Sources/FlaggedViews/FlaggedMessageView.swift b/Sources/FlaggedViews/FlaggedMessageView.swift new file mode 100644 index 0000000..8fd4e15 --- /dev/null +++ b/Sources/FlaggedViews/FlaggedMessageView.swift @@ -0,0 +1,32 @@ +import SharedModels +import SwiftUI + +public struct FlaggedMessageView: View { + + @Environment(\.flaggedMessageViewStyle) private var style + + let message: String? + let status: Flagged.CheckResult.Status + + public init(message: String?, status: Flagged.CheckResult.Status) { + self.message = message + self.status = status + } + + public var body: some View { + style.makeBody( + configuration: .init(message: message, status: status) + ) + } +} + +extension FlaggedMessageView { + + public init(flagged: Flagged) { + self.init(message: flagged.message, status: flagged.status) + } +} + +//#Preview { +// SwiftUIView() +//} diff --git a/Sources/Styleguide/FlaggedView.swift b/Sources/FlaggedViews/FlaggedView.swift similarity index 100% rename from Sources/Styleguide/FlaggedView.swift rename to Sources/FlaggedViews/FlaggedView.swift diff --git a/Sources/Styleguide/Styles/FlaggedEquipmentMeasurementStyle.swift b/Sources/FlaggedViews/Styles/FlaggedEquipmentMeasurementStyle.swift similarity index 100% rename from Sources/Styleguide/Styles/FlaggedEquipmentMeasurementStyle.swift rename to Sources/FlaggedViews/Styles/FlaggedEquipmentMeasurementStyle.swift diff --git a/Sources/FlaggedViews/Styles/FlaggedMessageViewStyle.swift b/Sources/FlaggedViews/Styles/FlaggedMessageViewStyle.swift new file mode 100644 index 0000000..8cf6048 --- /dev/null +++ b/Sources/FlaggedViews/Styles/FlaggedMessageViewStyle.swift @@ -0,0 +1,115 @@ +import SharedModels +import Styleguide +import SwiftUI + +public protocol FlaggedMessageViewStyle { + associatedtype Body: View + typealias Configuration = FlaggedMessageViewStyleConfiguration + + @ViewBuilder + func makeBody(configuration: Self.Configuration) -> Self.Body +} + +public struct FlaggedMessageViewStyleConfiguration { + public let message: String? + public let status: Flagged.CheckResult.Status + + public init(message: String?, status: Flagged.CheckResult.Status) { + self.message = message + self.status = status + } +} + +public struct AnyFlaggedMessageViewStyle: FlaggedMessageViewStyle { + private let _makeBody: (Configuration) -> AnyView + + internal init(makeBody: @escaping (Configuration) -> AnyView) { + self._makeBody = makeBody + } + + public init(_ style: S) { + self.init { configuration in + AnyView(style.makeBody(configuration: configuration)) + } + } + + public func makeBody(configuration: Configuration) -> some View { + _makeBody(configuration) + } +} + +public struct DefaultFlaggedMessageViewStyle: FlaggedMessageViewStyle { + + @ViewBuilder + public func makeBody(configuration: Configuration) -> some View { + if let message = configuration.message { + HStack { + Text(configuration.status.title) + .bold() + .foregroundStyle(configuration.status.flagColor) + TextLabel(message) + } + .font(.caption) + } + } +} + +public struct VerticalFlaggedMessageViewStyle: FlaggedMessageViewStyle { + + @ViewBuilder + public func makeBody(configuration: Configuration) -> some View { + if let message = configuration.message { + VStack(alignment: .leading) { + Text(configuration.status.title) + .bold() + .foregroundStyle(configuration.status.flagColor) + TextLabel(message) + } + .font(.caption) + } + } +} + +extension FlaggedMessageViewStyle where Self == VerticalFlaggedMessageViewStyle { + public static var vertical: Self { .init() } +} + +extension FlaggedMessageViewStyle where Self == DefaultFlaggedMessageViewStyle { + public static var horizontal: Self { .init() } + public static var `default`: Self { .init() } +} + +private struct FlaggedMessageViewStyleKey: EnvironmentKey { + static var defaultValue = AnyFlaggedMessageViewStyle(DefaultFlaggedMessageViewStyle()) +} + +extension EnvironmentValues { + public var flaggedMessageViewStyle: AnyFlaggedMessageViewStyle { + get { self[FlaggedMessageViewStyleKey.self] } + set { self[FlaggedMessageViewStyleKey.self] = newValue } + } +} + +extension View { + public func flaggedMessageViewStyle(_ style: AnyFlaggedMessageViewStyle) -> some View { + environment(\.flaggedMessageViewStyle, style) + } + + public func flaggedMessageViewStyle(_ style: S) -> some View { + flaggedMessageViewStyle(AnyFlaggedMessageViewStyle(style)) + } +} + +extension Flagged.CheckResult.Status { + + var flagColor: Color { + switch self { + case .good: + return .green + case .warning: + return .yellow + case .error: + return .red + } + } +} diff --git a/Sources/Styleguide/Styles/FlaggedViewStyle.swift b/Sources/FlaggedViews/Styles/FlaggedViewStyle.swift similarity index 82% rename from Sources/Styleguide/Styles/FlaggedViewStyle.swift rename to Sources/FlaggedViews/Styles/FlaggedViewStyle.swift index 3118039..9fd5b56 100644 --- a/Sources/Styleguide/Styles/FlaggedViewStyle.swift +++ b/Sources/FlaggedViews/Styles/FlaggedViewStyle.swift @@ -68,7 +68,7 @@ public struct DefaultFlagViewStyle: FlaggedViewStyle { ) configuration.flagged.flagImage } - flaggedMessageView(flagged: configuration.flagged) + FlaggedMessageView(flagged: configuration.flagged) } } } @@ -87,13 +87,13 @@ public struct FlagAndMessageOnlyStyle: FlaggedViewStyle { switch stackStyle { case .horizontal: HStack { - flaggedMessageView(flagged: configuration.flagged) + FlaggedMessageView(flagged: configuration.flagged) configuration.flagged.flagImage } case .vertical: VStack { configuration.flagged.flagImage - flaggedMessageView(flagged: configuration.flagged) + FlaggedMessageView(flagged: configuration.flagged) } } } @@ -114,7 +114,7 @@ public struct FlaggedGridRowStyle: FlaggedViewStyle { VStack(alignment: .leading, spacing: 8) { configuration.label .foregroundStyle(Color.secondary) - flaggedMessageView(flagged: configuration.flagged) + FlaggedMessageView(flagged: configuration.flagged) } Text( configuration.flagged.wrappedValue, @@ -149,23 +149,9 @@ extension FlaggedViewStyle where Self == DefaultFlagViewStyle { } } -fileprivate extension Flagged.CheckResult.Key { - - var flagColor: Color { - switch self { - case .good: - return .green - case .warning: - return .yellow - case .error: - return .red - } - } -} - extension Flagged { - var flagColor: Color { projectedValue.key.flagColor } + var flagColor: Color { self.status.flagColor } public var flagImage: some View { Image(systemName: "flag.fill") @@ -173,20 +159,7 @@ extension Flagged { } public var messageView: some View { - flaggedMessageView(flagged: self) - } -} - -@ViewBuilder -fileprivate func flaggedMessageView(flagged: Flagged) -> some View { - if let message = flagged.message { - HStack { - Text(flagged.projectedValue.key.title) - .bold() - .foregroundStyle(flagged.projectedValue.key.flagColor) - TextLabel(message) - } - .font(.caption) + FlaggedMessageView(flagged: self) } } diff --git a/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift b/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift index 87ecd75..05e0c10 100644 --- a/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift +++ b/Sources/PressureEstimationsFeature/EquipmentSettingsForm.swift @@ -1,4 +1,5 @@ import ComposableArchitecture +import FlaggedViews import SharedModels import Styleguide import SwiftUI diff --git a/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift b/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift index 232166c..37d1cd2 100644 --- a/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift +++ b/Sources/PressureEstimationsFeature/FlaggedMeasurementsList.swift @@ -1,6 +1,7 @@ import ComposableArchitecture import DependenciesAdditions import EstimatedPressureDependency +import FlaggedViews import SharedModels import Styleguide import SwiftUI @@ -205,6 +206,7 @@ public struct FlaggedMeasurementsList { @ViewAction(for: FlaggedMeasurementsList.self) public struct FlaggedMeasurementListView: View { + @Environment(\.horizontalSizeClass) var horizontalSizeClass @Bindable public var store: StoreOf public init(store: StoreOf) { @@ -265,6 +267,11 @@ public struct FlaggedMeasurementListView: View { } } .onAppear { send(.onAppear) } + .flaggedMessageViewStyle( + horizontalSizeClass == .compact + ? AnyFlaggedMessageViewStyle(.vertical) + : AnyFlaggedMessageViewStyle(.horizontal) + ) } } diff --git a/Sources/SharedModels/Flagged.swift b/Sources/SharedModels/Flagged.swift index efc20e5..fce4bbc 100644 --- a/Sources/SharedModels/Flagged.swift +++ b/Sources/SharedModels/Flagged.swift @@ -70,7 +70,7 @@ public struct Flagged: Equatable { case good(String? = nil) case error(String) - public var key: Key { + public var status: Status { switch self { case .aboveMaximum(_): return .error @@ -110,7 +110,7 @@ public struct Flagged: Equatable { } } - public enum Key: String, Equatable, CaseIterable { + public enum Status: String, Equatable, CaseIterable { case good, warning, error public var title: String {