feat: Moves flagged views into their own module.
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public struct FlaggedEquipmentMeasurementView: View {
|
||||
|
||||
@Environment(\.flaggedEquipmentMeasurementStyle) private var style
|
||||
|
||||
let measurement: FlaggedEquipmentMeasurement
|
||||
|
||||
public init(_ measurement: FlaggedEquipmentMeasurement) {
|
||||
self.measurement = measurement
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
style.makeBody(
|
||||
configuration: FlaggedEquipmentMeasurementStyleConfiguration(
|
||||
measurement: measurement
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private let equipmentMeasurement = EquipmentMeasurement.airHandler(.init(
|
||||
airflow: 1600,
|
||||
returnPlenumPressure: 0.37,
|
||||
postFilterPressure: 0.78,
|
||||
postCoilPressure: 0.9,
|
||||
supplyPlenumPressure: 0.11
|
||||
))
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
FlaggedEquipmentMeasurementView(
|
||||
.init(
|
||||
budgets: .init(equipmentType: .airHandler, fanType: .variableSpeed),
|
||||
measurement: equipmentMeasurement,
|
||||
ratedPressures: .init(),
|
||||
tons: .four
|
||||
)
|
||||
)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,40 +0,0 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public struct FlaggedView<Label: View>: View {
|
||||
|
||||
@Environment(\.flaggedViewStyle) private var flaggedViewStyle
|
||||
|
||||
let label: () -> Label
|
||||
let flagged: Flagged
|
||||
|
||||
public init(
|
||||
flagged: Flagged,
|
||||
@ViewBuilder label: @escaping () -> Label
|
||||
) {
|
||||
self.label = label
|
||||
self.flagged = flagged
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
flaggedViewStyle.makeBody(
|
||||
configuration: FlaggedViewStyleConfiguration(flagged: flagged, label: .init(label()))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedView where Label == Text {
|
||||
public init(_ title: LocalizedStringKey, flagged: Flagged) {
|
||||
self.init(flagged: flagged) { Text(title) }
|
||||
}
|
||||
|
||||
public init<S: StringProtocol>(_ title: S, flagged: Flagged) {
|
||||
self.init(flagged: flagged) { Text(title) }
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedView where Label == EmptyView {
|
||||
public init(flagged: Flagged) {
|
||||
self.init(flagged: flagged) { EmptyView() }
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public protocol FlaggedEquipmentMeasurementStyle {
|
||||
associatedtype Body: View
|
||||
typealias Configuration = FlaggedEquipmentMeasurementStyleConfiguration
|
||||
|
||||
@ViewBuilder
|
||||
func makeBody(configuration: Self.Configuration) -> Self.Body
|
||||
}
|
||||
|
||||
public struct FlaggedEquipmentMeasurementStyleConfiguration {
|
||||
public let measurement: FlaggedEquipmentMeasurement
|
||||
}
|
||||
|
||||
public struct AnyFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle {
|
||||
private var _makeBody: (Configuration) -> AnyView
|
||||
|
||||
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
||||
self._makeBody = makeBody
|
||||
}
|
||||
|
||||
public init<S: FlaggedEquipmentMeasurementStyle>(_ style: S) {
|
||||
self.init { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
_makeBody(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
public struct GridFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle {
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
Grid(alignment: .leading, verticalSpacing: 20) {
|
||||
ForEach(FlaggedEquipmentMeasurement.Key.allCases) { field in
|
||||
FlaggedView(
|
||||
field.title,
|
||||
flagged: configuration.measurement[keyPath: field.flaggedKeyPath]
|
||||
)
|
||||
.flaggedViewStyle(.gridRow(fractionLength: field == .airflow ? 0 : 2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedEquipmentMeasurementStyle where Self == GridFlaggedEquipmentMeasurementStyle {
|
||||
public static var grid: Self { .init() }
|
||||
}
|
||||
|
||||
private struct FlaggedEquipmentMeasurementStyleKey: EnvironmentKey {
|
||||
static let defaultValue = AnyFlaggedEquipmentMeasurementStyle(GridFlaggedEquipmentMeasurementStyle())
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
public var flaggedEquipmentMeasurementStyle: AnyFlaggedEquipmentMeasurementStyle {
|
||||
get { self[FlaggedEquipmentMeasurementStyleKey.self] }
|
||||
set { self[FlaggedEquipmentMeasurementStyleKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func flaggedEquipmentMeasurementStyle(_ style: AnyFlaggedEquipmentMeasurementStyle) -> some View {
|
||||
environment(\.flaggedEquipmentMeasurementStyle, style)
|
||||
}
|
||||
|
||||
public func flaggedEquipmentMeasurementStyle<S: FlaggedEquipmentMeasurementStyle>(
|
||||
_ style: S
|
||||
) -> some View {
|
||||
flaggedEquipmentMeasurementStyle(AnyFlaggedEquipmentMeasurementStyle(style))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Key
|
||||
fileprivate extension FlaggedEquipmentMeasurement {
|
||||
// NOTE: These need to be kept in display order.
|
||||
enum Key: Hashable, CaseIterable, Identifiable {
|
||||
case returnPlenum
|
||||
case filterDrop
|
||||
case coilDrop
|
||||
case supplyPlenum
|
||||
case staticPressure
|
||||
case airflow
|
||||
|
||||
var id: Self { self }
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .returnPlenum:
|
||||
return "Return Plenum"
|
||||
case .filterDrop:
|
||||
return "Filter Pressure Drop"
|
||||
case .coilDrop:
|
||||
return "Coil Pressure Drop"
|
||||
case .supplyPlenum:
|
||||
return "Supply Plenum"
|
||||
case .staticPressure:
|
||||
return "External Static Pressure"
|
||||
case .airflow:
|
||||
return "System Airflow"
|
||||
}
|
||||
}
|
||||
|
||||
var flaggedKeyPath: KeyPath<FlaggedEquipmentMeasurement, Flagged> {
|
||||
switch self {
|
||||
case .returnPlenum:
|
||||
return \.returnPlenumPressure
|
||||
case .filterDrop:
|
||||
return \.filterPressureDrop
|
||||
case .coilDrop:
|
||||
return \.coilPressureDrop
|
||||
case .supplyPlenum:
|
||||
return \.supplyPlenumPressure
|
||||
case .staticPressure:
|
||||
return \.externalStaticPressure
|
||||
case .airflow:
|
||||
return \.airflow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public protocol FlaggedViewStyle {
|
||||
associatedtype Body: View
|
||||
typealias Configuration = FlaggedViewStyleConfiguration
|
||||
|
||||
@ViewBuilder
|
||||
func makeBody(configuration: Self.Configuration) -> Self.Body
|
||||
}
|
||||
|
||||
public struct FlaggedViewStyleConfiguration {
|
||||
public let flagged: Flagged
|
||||
public let label: Label
|
||||
|
||||
/// A type erased label for a flagged view.
|
||||
public struct Label: View {
|
||||
public let body: AnyView
|
||||
|
||||
public init<Content: View>(_ content: Content) {
|
||||
self.body = AnyView(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct AnyFlaggedViewStyle: FlaggedViewStyle {
|
||||
private var _makeBody: (Configuration) -> AnyView
|
||||
|
||||
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
||||
self._makeBody = makeBody
|
||||
}
|
||||
|
||||
public init<S: FlaggedViewStyle>(style: S) {
|
||||
self.init { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
_makeBody(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultFlagViewStyle: FlaggedViewStyle {
|
||||
|
||||
let alignment: HorizontalAlignment
|
||||
let fractionLength: Int
|
||||
let spacing: CGFloat
|
||||
|
||||
init(
|
||||
alignment: HorizontalAlignment = .leading,
|
||||
fractionLength: Int = 2,
|
||||
spacing: CGFloat = 8
|
||||
) {
|
||||
self.alignment = alignment
|
||||
self.fractionLength = fractionLength
|
||||
self.spacing = spacing
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
VStack(alignment: alignment, spacing: spacing) {
|
||||
HStack {
|
||||
configuration.label
|
||||
Spacer()
|
||||
Text(
|
||||
configuration.flagged.wrappedValue,
|
||||
format: .number.precision(.fractionLength(fractionLength))
|
||||
)
|
||||
configuration.flagged.flagImage
|
||||
}
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct FlagAndMessageOnlyStyle: FlaggedViewStyle {
|
||||
|
||||
public enum StackStyle {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
let stackStyle: StackStyle
|
||||
|
||||
@ViewBuilder
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
switch stackStyle {
|
||||
case .horizontal:
|
||||
HStack {
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
configuration.flagged.flagImage
|
||||
}
|
||||
case .vertical:
|
||||
VStack {
|
||||
configuration.flagged.flagImage
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedViewStyle where Self == FlagAndMessageOnlyStyle {
|
||||
public static func flagAndMessageOnly(_ stackStyle: FlagAndMessageOnlyStyle.StackStyle = .vertical) -> Self {
|
||||
.init(stackStyle: stackStyle)
|
||||
}
|
||||
}
|
||||
|
||||
public struct FlaggedGridRowStyle: FlaggedViewStyle {
|
||||
|
||||
let fractionLength: Int
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
GridRow {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
configuration.label
|
||||
.foregroundStyle(Color.secondary)
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
}
|
||||
Text(
|
||||
configuration.flagged.wrappedValue,
|
||||
format: .number.precision(.fractionLength(fractionLength))
|
||||
)
|
||||
configuration.flagged.flagImage
|
||||
.gridCellAnchor(.trailing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedViewStyle where Self == FlaggedGridRowStyle {
|
||||
public static func gridRow(fractionLength: Int = 2) -> Self {
|
||||
.init(fractionLength: fractionLength)
|
||||
}
|
||||
}
|
||||
|
||||
private struct FlaggedViewStyleKey: EnvironmentKey {
|
||||
static let defaultValue = AnyFlaggedViewStyle(style: DefaultFlagViewStyle())
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
public var flaggedViewStyle: AnyFlaggedViewStyle {
|
||||
get { self[FlaggedViewStyleKey.self] }
|
||||
set { self[FlaggedViewStyleKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedViewStyle where Self == DefaultFlagViewStyle {
|
||||
public static func `default`(alignment: HorizontalAlignment = .leading, fractionLength: Int = 2, spacing: CGFloat = 8) -> Self {
|
||||
.init(alignment: alignment, fractionLength: fractionLength, spacing: spacing)
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
public var flagImage: some View {
|
||||
Image(systemName: "flag.fill")
|
||||
.foregroundStyle(flagColor)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func flaggedViewStyle(_ style: AnyFlaggedViewStyle) -> some View {
|
||||
environment(\.flaggedViewStyle, style)
|
||||
}
|
||||
|
||||
public func flaggedViewStyle<S: FlaggedViewStyle>(
|
||||
_ style: S
|
||||
) -> some View {
|
||||
flaggedViewStyle(AnyFlaggedViewStyle(style: style))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user