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 ) ) } } 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: 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( _ 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(_ 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(_ 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)) } }