Files
swift-estimated-pressures-core/Sources/FlaggedViews/FlaggedStatusLabel.swift

172 lines
4.4 KiB
Swift

import SharedModels
import Styleguide
import SwiftUI
/// Represents the status of a flagged value, which is stylable using the
/// `.flaggedStatusLabelStyle` modifier on a view.
///
/// By default the status label is colored using the default status color.
public struct FlaggedStatusLabel: View {
@Environment(\.flaggedStatusLabelStyle) private var style
let status: Flagged.CheckResult.Status
public init(status: Flagged.CheckResult.Status) {
self.status = status
}
public var body: some View {
style.makeBody(
configuration: FlaggedStatusLabelStyleConfiguration(
status: status
)
)
}
}
@MainActor
@preconcurrency
public protocol FlaggedStatusLabelStyle {
associatedtype Body: View
typealias Configuration = FlaggedStatusLabelStyleConfiguration
@ViewBuilder
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
}
@MainActor
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?
@MainActor
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: @preconcurrency EnvironmentKey {
@MainActor static let 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 {
VStack {
FlaggedStatusLabel(status: .warning)
.flaggedStatusLabelStyle(.textLabel(.heavyTitle2))
FlaggedStatusLabel(status: .warning)
.flaggedStatusLabelStyle(.pill)
// FlaggedStatusLabel(status: .warning
// .flaggedStatusLabelStyle(.pill(.heavyTitle2))
}
}