feat: Moves flagged styles into their view files
This commit is contained in:
@@ -20,6 +20,128 @@ public struct FlaggedEquipmentMeasurementView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private let equipmentMeasurement = EquipmentMeasurement.airHandler(.init(
|
private let equipmentMeasurement = EquipmentMeasurement.airHandler(.init(
|
||||||
airflow: 1600,
|
airflow: 1600,
|
||||||
|
|||||||
@@ -22,6 +22,28 @@ public struct FlaggedMessageLabel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct FlaggedMessageLabelStyleKey: EnvironmentKey {
|
||||||
|
static var defaultValue = AnyTextLabelStyle(style: .font(.caption))
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
var flaggedMessageLabelStyle: AnyTextLabelStyle {
|
||||||
|
get { self[FlaggedMessageLabelStyleKey.self] }
|
||||||
|
set { self[FlaggedMessageLabelStyleKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
public func flaggedMessageLabelStyle(_ style: AnyTextLabelStyle) -> some View {
|
||||||
|
environment(\.flaggedMessageLabelStyle, style)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func flaggedMessageLabelStyle<S: TextLabelStyle>(_ style: S) -> some View {
|
||||||
|
flaggedMessageLabelStyle(AnyTextLabelStyle(style: style))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
FlaggedStatusLabel(status: .warning)
|
FlaggedStatusLabel(status: .warning)
|
||||||
.flaggedStatusLabelStyle(.textLabel(.heavyTitle2))
|
.flaggedStatusLabelStyle(.textLabel(.heavyTitle2))
|
||||||
|
|||||||
@@ -26,3 +26,130 @@ extension FlaggedMessageView {
|
|||||||
self.init(message: flagged.message, status: flagged.status)
|
self.init(message: flagged.message, status: flagged.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<S: FlaggedMessageViewStyle>(_ style: S) {
|
||||||
|
self.init { configuration in
|
||||||
|
AnyView(style.makeBody(configuration: configuration))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
_makeBody(configuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct HorizontalFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
if configuration.message != nil {
|
||||||
|
HStack {
|
||||||
|
FlaggedStatusLabel(status: configuration.status)
|
||||||
|
FlaggedMessageLabel(message: configuration.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct VerticalFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
if configuration.message != nil {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
FlaggedStatusLabel(status: configuration.status)
|
||||||
|
FlaggedMessageLabel(message: configuration.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct AutomaticFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
||||||
|
|
||||||
|
let horizontalSizeClass: UserInterfaceSizeClass?
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
if horizontalSizeClass == .compact {
|
||||||
|
VerticalFlaggedMessageViewStyle().makeBody(configuration: configuration)
|
||||||
|
} else {
|
||||||
|
HorizontalFlaggedMessageViewStyle().makeBody(configuration: configuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FlaggedMessageViewStyle where Self == VerticalFlaggedMessageViewStyle {
|
||||||
|
public static var vertical: Self { .init() }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FlaggedMessageViewStyle where Self == HorizontalFlaggedMessageViewStyle {
|
||||||
|
public static var horizontal: Self { .init() }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FlaggedMessageViewStyle where Self == AutomaticFlaggedMessageViewStyle {
|
||||||
|
public static func automatic(horizontalSizeClass: UserInterfaceSizeClass? = nil) -> Self {
|
||||||
|
.init(horizontalSizeClass: horizontalSizeClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct FlaggedMessageViewStyleKey: EnvironmentKey {
|
||||||
|
static var defaultValue = AnyFlaggedMessageViewStyle(
|
||||||
|
AutomaticFlaggedMessageViewStyle(horizontalSizeClass: nil)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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<S: FlaggedMessageViewStyle>(_ style: S) -> some View {
|
||||||
|
flaggedMessageViewStyle(AnyFlaggedMessageViewStyle(style))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Flagged.CheckResult.Status {
|
||||||
|
|
||||||
|
public var color: Color {
|
||||||
|
switch self {
|
||||||
|
case .good:
|
||||||
|
return .green
|
||||||
|
case .warning:
|
||||||
|
return .yellow
|
||||||
|
case .error:
|
||||||
|
return .red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,136 @@ public struct FlaggedStatusLabel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public protocol FlaggedStatusLabelStyle {
|
||||||
|
associatedtype Body: View
|
||||||
|
typealias Configuration = FlaggedStatusLabelStyleConfiguration
|
||||||
|
|
||||||
|
func makeBody(configuration: Self.Configuration) -> Self.Body
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FlaggedStatusLabelStyleConfiguration {
|
||||||
|
public let status: Flagged.CheckResult.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct AnyFlaggedStatusLabelStyle: FlaggedStatusLabelStyle {
|
||||||
|
private let _makeBody: (Configuration) -> AnyView
|
||||||
|
|
||||||
|
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
||||||
|
self._makeBody = makeBody
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<Style: FlaggedStatusLabelStyle>(_ style: Style) {
|
||||||
|
self.init { configuration in
|
||||||
|
AnyView(style.makeBody(configuration: configuration))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
_makeBody(configuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FlaggedStatusTextLabelStyle: FlaggedStatusLabelStyle {
|
||||||
|
|
||||||
|
let textLabelStyle: AnyTextLabelStyle?
|
||||||
|
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
TextLabel(configuration.status.title)
|
||||||
|
.textLabelStyle(
|
||||||
|
textLabelStyle
|
||||||
|
?? AnyTextLabelStyle(
|
||||||
|
style: .colored(configuration.status.color).font(.caption, fontWeight: .bold)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct FlaggedStatusLabelStyleKey: EnvironmentKey {
|
||||||
|
static var defaultValue = AnyFlaggedStatusLabelStyle(
|
||||||
|
FlaggedStatusTextLabelStyle(
|
||||||
|
textLabelStyle: AnyTextLabelStyle(
|
||||||
|
style: .font(.callout, fontWeight: .bold)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FlaggedStatusLabelStyle where Self == FlaggedStatusTextLabelStyle {
|
||||||
|
|
||||||
|
public static var textLabel: Self { textLabel() }
|
||||||
|
|
||||||
|
public static func textLabel(
|
||||||
|
_ style: AnyTextLabelStyle? = nil
|
||||||
|
) -> Self {
|
||||||
|
.init(textLabelStyle: style)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func textLabel<S: TextLabelStyle>(
|
||||||
|
_ style: S
|
||||||
|
) -> Self {
|
||||||
|
.init(textLabelStyle: AnyTextLabelStyle(style: style))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FlaggedStatusPillStyle: FlaggedStatusLabelStyle {
|
||||||
|
let labelStyle: AnyTextLabelStyle?
|
||||||
|
let opacity: Double
|
||||||
|
|
||||||
|
public func makeBody(configuration: Configuration) -> some View {
|
||||||
|
TextLabel(configuration.status.title)
|
||||||
|
.textLabelStyle(labelStyle ?? AnyTextLabelStyle(
|
||||||
|
style: .font(.caption, fontWeight: .bold).combining(.colored(.white))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.background {
|
||||||
|
Capsule()
|
||||||
|
.foregroundStyle(configuration.status.color.opacity(opacity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FlaggedStatusLabelStyle where Self == FlaggedStatusPillStyle {
|
||||||
|
|
||||||
|
public static var pill: Self { .pill() }
|
||||||
|
|
||||||
|
public static func pill(
|
||||||
|
_ style: AnyTextLabelStyle? = nil,
|
||||||
|
opacity: Double = 1
|
||||||
|
) -> Self {
|
||||||
|
.init(labelStyle: style, opacity: opacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func pill<S: TextLabelStyle>(_ style: S, opacity: Double = 1) -> Self {
|
||||||
|
.init(labelStyle: AnyTextLabelStyle(style: style), opacity: opacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
public var flaggedStatusLabelStyle: AnyFlaggedStatusLabelStyle {
|
||||||
|
get { self[FlaggedStatusLabelStyleKey.self] }
|
||||||
|
set { self[FlaggedStatusLabelStyleKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
public func flaggedStatusLabelStyle(
|
||||||
|
_ style: AnyFlaggedStatusLabelStyle
|
||||||
|
) -> some View {
|
||||||
|
environment(
|
||||||
|
\.flaggedStatusLabelStyle,
|
||||||
|
style
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public func flaggedStatusLabelStyle<S: FlaggedStatusLabelStyle>(_ style: S) -> some View {
|
||||||
|
flaggedStatusLabelStyle(AnyFlaggedStatusLabelStyle(style))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
VStack {
|
VStack {
|
||||||
FlaggedStatusLabel(status: .warning)
|
FlaggedStatusLabel(status: .warning)
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ public struct FlaggedView<Label: View>: View {
|
|||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
flaggedViewStyle.makeBody(
|
flaggedViewStyle.makeBody(
|
||||||
configuration: FlaggedViewStyleConfiguration(flagged: flagged, label: .init(label()))
|
configuration: FlaggedViewStyleConfiguration(
|
||||||
|
flagged: flagged,
|
||||||
|
label: .init(label())
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,3 +41,180 @@ extension FlaggedView where Label == EmptyView {
|
|||||||
self.init(flagged: flagged) { EmptyView() }
|
self.init(flagged: flagged) { EmptyView() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension Flagged {
|
||||||
|
public var statusColor: Color { self.status.color }
|
||||||
|
|
||||||
|
public var flagImage: some View {
|
||||||
|
Image(systemName: "flag.fill")
|
||||||
|
.foregroundStyle(statusColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var messageView: some View {
|
||||||
|
FlaggedMessageView(flagged: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,24 +0,0 @@
|
|||||||
import Styleguide
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
private struct FlaggedMessageLabelStyleKey: EnvironmentKey {
|
|
||||||
static var defaultValue = AnyTextLabelStyle(style: .font(.caption))
|
|
||||||
}
|
|
||||||
|
|
||||||
extension EnvironmentValues {
|
|
||||||
var flaggedMessageLabelStyle: AnyTextLabelStyle {
|
|
||||||
get { self[FlaggedMessageLabelStyleKey.self] }
|
|
||||||
set { self[FlaggedMessageLabelStyleKey.self] = newValue }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
public func flaggedMessageLabelStyle(_ style: AnyTextLabelStyle) -> some View {
|
|
||||||
environment(\.flaggedMessageLabelStyle, style)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func flaggedMessageLabelStyle<S: TextLabelStyle>(_ style: S) -> some View {
|
|
||||||
flaggedMessageLabelStyle(AnyTextLabelStyle(style: style))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
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<S: FlaggedMessageViewStyle>(_ style: S) {
|
|
||||||
self.init { configuration in
|
|
||||||
AnyView(style.makeBody(configuration: configuration))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
_makeBody(configuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct HorizontalFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
if configuration.message != nil {
|
|
||||||
HStack {
|
|
||||||
FlaggedStatusLabel(status: configuration.status)
|
|
||||||
FlaggedMessageLabel(message: configuration.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct VerticalFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
if configuration.message != nil {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
FlaggedStatusLabel(status: configuration.status)
|
|
||||||
FlaggedMessageLabel(message: configuration.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AutomaticFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
||||||
|
|
||||||
let horizontalSizeClass: UserInterfaceSizeClass?
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
if horizontalSizeClass == .compact {
|
|
||||||
VerticalFlaggedMessageViewStyle().makeBody(configuration: configuration)
|
|
||||||
} else {
|
|
||||||
HorizontalFlaggedMessageViewStyle().makeBody(configuration: configuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FlaggedMessageViewStyle where Self == VerticalFlaggedMessageViewStyle {
|
|
||||||
public static var vertical: Self { .init() }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FlaggedMessageViewStyle where Self == HorizontalFlaggedMessageViewStyle {
|
|
||||||
public static var horizontal: Self { .init() }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FlaggedMessageViewStyle where Self == AutomaticFlaggedMessageViewStyle {
|
|
||||||
public static func automatic(horizontalSizeClass: UserInterfaceSizeClass? = nil) -> Self {
|
|
||||||
.init(horizontalSizeClass: horizontalSizeClass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct FlaggedMessageViewStyleKey: EnvironmentKey {
|
|
||||||
static var defaultValue = AnyFlaggedMessageViewStyle(
|
|
||||||
AutomaticFlaggedMessageViewStyle(horizontalSizeClass: nil)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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<S: FlaggedMessageViewStyle>(_ style: S) -> some View {
|
|
||||||
flaggedMessageViewStyle(AnyFlaggedMessageViewStyle(style))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Flagged.CheckResult.Status {
|
|
||||||
|
|
||||||
public var color: Color {
|
|
||||||
switch self {
|
|
||||||
case .good:
|
|
||||||
return .green
|
|
||||||
case .warning:
|
|
||||||
return .yellow
|
|
||||||
case .error:
|
|
||||||
return .red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import SharedModels
|
|
||||||
import Styleguide
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
public protocol FlaggedStatusLabelStyle {
|
|
||||||
associatedtype Body: View
|
|
||||||
typealias Configuration = FlaggedStatusLabelStyleConfiguration
|
|
||||||
|
|
||||||
func makeBody(configuration: Self.Configuration) -> Self.Body
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct FlaggedStatusLabelStyleConfiguration {
|
|
||||||
public let status: Flagged.CheckResult.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AnyFlaggedStatusLabelStyle: FlaggedStatusLabelStyle {
|
|
||||||
private let _makeBody: (Configuration) -> AnyView
|
|
||||||
|
|
||||||
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
|
||||||
self._makeBody = makeBody
|
|
||||||
}
|
|
||||||
|
|
||||||
public init<Style: FlaggedStatusLabelStyle>(_ style: Style) {
|
|
||||||
self.init { configuration in
|
|
||||||
AnyView(style.makeBody(configuration: configuration))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
_makeBody(configuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct FlaggedStatusTextLabelStyle: FlaggedStatusLabelStyle {
|
|
||||||
|
|
||||||
let textLabelStyle: AnyTextLabelStyle?
|
|
||||||
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
TextLabel(configuration.status.title)
|
|
||||||
.textLabelStyle(
|
|
||||||
textLabelStyle
|
|
||||||
?? AnyTextLabelStyle(
|
|
||||||
style: .colored(configuration.status.color).font(.caption, fontWeight: .bold)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct FlaggedStatusLabelStyleKey: EnvironmentKey {
|
|
||||||
static var defaultValue = AnyFlaggedStatusLabelStyle(
|
|
||||||
FlaggedStatusTextLabelStyle(
|
|
||||||
textLabelStyle: AnyTextLabelStyle(
|
|
||||||
style: .font(.callout, fontWeight: .bold)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FlaggedStatusLabelStyle where Self == FlaggedStatusTextLabelStyle {
|
|
||||||
|
|
||||||
public static var textLabel: Self { textLabel() }
|
|
||||||
|
|
||||||
public static func textLabel(
|
|
||||||
_ style: AnyTextLabelStyle? = nil
|
|
||||||
) -> Self {
|
|
||||||
.init(textLabelStyle: style)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func textLabel<S: TextLabelStyle>(
|
|
||||||
_ style: S
|
|
||||||
) -> Self {
|
|
||||||
.init(textLabelStyle: AnyTextLabelStyle(style: style))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct FlaggedStatusPillStyle: FlaggedStatusLabelStyle {
|
|
||||||
let labelStyle: AnyTextLabelStyle?
|
|
||||||
let opacity: Double
|
|
||||||
|
|
||||||
public func makeBody(configuration: Configuration) -> some View {
|
|
||||||
TextLabel(configuration.status.title)
|
|
||||||
.textLabelStyle(labelStyle ?? AnyTextLabelStyle(
|
|
||||||
style: .font(.caption, fontWeight: .bold).combining(.colored(.white))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.background {
|
|
||||||
Capsule()
|
|
||||||
.foregroundStyle(configuration.status.color.opacity(opacity))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FlaggedStatusLabelStyle where Self == FlaggedStatusPillStyle {
|
|
||||||
|
|
||||||
public static var pill: Self { .pill() }
|
|
||||||
|
|
||||||
public static func pill(
|
|
||||||
_ style: AnyTextLabelStyle? = nil,
|
|
||||||
opacity: Double = 1
|
|
||||||
) -> Self {
|
|
||||||
.init(labelStyle: style, opacity: opacity)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func pill<S: TextLabelStyle>(_ style: S, opacity: Double = 1) -> Self {
|
|
||||||
.init(labelStyle: AnyTextLabelStyle(style: style), opacity: opacity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension EnvironmentValues {
|
|
||||||
public var flaggedStatusLabelStyle: AnyFlaggedStatusLabelStyle {
|
|
||||||
get { self[FlaggedStatusLabelStyleKey.self] }
|
|
||||||
set { self[FlaggedStatusLabelStyleKey.self] = newValue }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
public func flaggedStatusLabelStyle(
|
|
||||||
_ style: AnyFlaggedStatusLabelStyle
|
|
||||||
) -> some View {
|
|
||||||
environment(
|
|
||||||
\.flaggedStatusLabelStyle,
|
|
||||||
style
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public func flaggedStatusLabelStyle<S: FlaggedStatusLabelStyle>(_ style: S) -> some View {
|
|
||||||
flaggedStatusLabelStyle(AnyFlaggedStatusLabelStyle(style))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,179 +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)
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
HStack(spacing: 10) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension Flagged {
|
|
||||||
public var statusColor: Color { self.status.color }
|
|
||||||
|
|
||||||
public var flagImage: some View {
|
|
||||||
Image(systemName: "flag.fill")
|
|
||||||
.foregroundStyle(statusColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
public var messageView: some View {
|
|
||||||
FlaggedMessageView(flagged: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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