import SwiftUI public protocol TextLabelStyle { associatedtype Body: View typealias Configuration = TextLabelConfiguration func makeBody(configuration: Self.Configuration) -> Self.Body } extension TextLabelStyle { public func combining(_ style: Other) -> AnyTextLabelStyle { AnyTextLabelStyle(style: self).combining(style) } } public struct TextLabelConfiguration { /// A type erased label for a text label. public struct Label: View { public var body: AnyView public init(content: Content) { body = AnyView(content) } } public let label: TextLabelConfiguration.Label } public struct AnyTextLabelStyle: TextLabelStyle { private var _makeBody: (Configuration) -> AnyView internal init(makeBody: @escaping (Configuration) -> AnyView) { self._makeBody = makeBody } public init(style: S) { self._makeBody = { configuration in AnyView(style.makeBody(configuration: configuration)) } } public func makeBody(configuration: Configuration) -> some View { _makeBody(configuration) } public func combining(_ style: S) -> Self { Self.init { configuration in AnyView( self.makeBody( configuration: TextLabelConfiguration( label: .init(content: style.makeBody(configuration: configuration)) ) )) } } } public struct AutomaticTextLabelStyle: TextLabelStyle { public func makeBody(configuration: Configuration) -> some View { configuration.label } } private struct TextLabelStyleKey: EnvironmentKey { static let defaultValue = AnyTextLabelStyle(style: AutomaticTextLabelStyle()) } private struct SectionHeaderLabelStyleKey: EnvironmentKey { static let defaultValue = AnyTextLabelStyle(style: AutomaticTextLabelStyle()) } extension EnvironmentValues { public var textLabelStyle: AnyTextLabelStyle { get { self[TextLabelStyleKey.self] } set { self[TextLabelStyleKey.self] = newValue } } public var sectionHeaderLabelStyle: AnyTextLabelStyle { get { self[SectionHeaderLabelStyleKey.self] } set { self[SectionHeaderLabelStyleKey.self] = newValue } } } public struct FontTextLabelStyle: TextLabelStyle { let font: Font? let fontWeight: Font.Weight? public func makeBody(configuration: Configuration) -> some View { configuration.label .font(font) .fontWeight(fontWeight) } } public struct ColoredTextLabelStyle: TextLabelStyle { let primary: Color let secondary: Color? let tertiary: Color? @ViewBuilder public func makeBody(configuration: Configuration) -> some View { switch (secondary, tertiary) { case let (.some(secondary), .some(tertiary)): configuration.label.foregroundStyle(primary, secondary, tertiary) case let (.some(secondary), .none): configuration.label.foregroundStyle(primary, secondary) case let (.none, .some(tertiary)): configuration.label.foregroundStyle(primary, tertiary) case (.none, .none): configuration.label.foregroundStyle(primary) } } public func font(_ font: Font? = nil, fontWeight: Font.Weight? = nil) -> AnyTextLabelStyle { self.combining(.font(font, fontWeight: fontWeight)) } } extension TextLabelStyle where Self == AutomaticTextLabelStyle { public static var automatic: Self { .init() } } extension TextLabelStyle where Self == FontTextLabelStyle { public static func font(_ font: Font? = nil, fontWeight: Font.Weight? = nil) -> Self { .init(font: font, fontWeight: fontWeight) } public static var heavyTitle2: Self { .font(.title2, fontWeight: .heavy) } } extension TextLabelStyle where Self == ColoredTextLabelStyle { public static func colored(_ color: Color) -> Self { .init(primary: color, secondary: nil, tertiary: nil) } public static func colored(_ primary: Color, _ secondary: Color, _ tertiary: Color? = nil) -> Self { .init(primary: primary, secondary: secondary, tertiary: tertiary) } public static var secondary: Self { self.colored(.secondary) } } extension AnyTextLabelStyle { public static var boldSecondary: AnyTextLabelStyle { ColoredTextLabelStyle(primary: .secondary, secondary: nil, tertiary: nil) .combining(.font(fontWeight: .bold)) } } public struct PillViewTextLabelStyle: TextLabelStyle { let labelColor: Color? let pillColor: Color public func makeBody(configuration: Configuration) -> some View { configuration.label .foregroundStyle(labelColor ?? .primary) .background { Capsule() .foregroundStyle(pillColor) } } } extension TextLabelStyle where Self == PillViewTextLabelStyle { public static func pill( pillColor: Color, labelColor: Color? = nil ) -> Self { .init(labelColor: labelColor, pillColor: pillColor) } } extension View { public func textLabelStyle(_ style: AnyTextLabelStyle) -> some View { environment(\.textLabelStyle, style) } public func textLabelStyle(_ style: S) -> some View { textLabelStyle(AnyTextLabelStyle(style: style)) } public func sectionHeaderLabelStyle(_ style: S) -> some View { environment(\.sectionHeaderLabelStyle, AnyTextLabelStyle(style: style)) } }