import SwiftUI /// The label style for the transient in-app notification banners. /// /// The style renders the label's icon next to its title, over a padded, capsule Liquid Glass background. The icon is tinted with a color /// matching the kind of notification given at initialization — green for an informational one, yellow for a warning, and red for an /// error — and the title wears a medium-weight callout font. public struct NotificationLabelStyle: LabelStyle { // MARK: Properties /// The kind of notification whose matching color tints the label's icon. private let kind: AppNotification.Kind // MARK: Initializers /// Creates a notification label style for a given kind of notification. /// /// - Parameter kind: The kind of notification whose matching color tints the label's icon. public init( kind: AppNotification.Kind ) { self.kind = kind } // MARK: Methods /// Creates the styled body of the label. /// /// - Parameter configuration: The properties of the label being styled. public func makeBody( configuration: Configuration ) -> some View { HStack( spacing: Constant.Spacing.stack ) { configuration.icon .font(.title3) .foregroundStyle(kind.imageStyle) configuration.title .font(.callout) .fontWeight(.medium) .foregroundStyle(.primary) } .padding(.vertical, Constant.Padding.vertical) .padding(.horizontal, Constant.Padding.horizontal) .glassEffect( .regular, in: .capsule ) } } // MARK: - Styles public extension LabelStyle where Self == NotificationLabelStyle { /// The label style for the transient in-app notification banners. /// /// - Parameter kind: The kind of notification whose matching color tints the label's icon. /// - Returns: A notification label style for the given kind of notification. static func notification( kind: AppNotification.Kind ) -> NotificationLabelStyle { .init(kind: kind) } } // MARK: - AppNotification.Kind+Properties private extension AppNotification.Kind { /// The color that tints the icon of a notification label: green for an informational notification, yellow for a warning, and red /// for an error. var imageStyle: Color { switch self { case .error: .red case .info: .green case .warning: .yellow } } } // MARK: - Constants /// The constant values used across the label style. private enum Constant { /// The padding constants. enum Padding { /// The horizontal padding between a label's content and its background. static let horizontal: CGFloat = 16 /// The vertical padding between a label's content and its background. static let vertical: CGFloat = 8 } /// The spacing constants. enum Spacing { /// The spacing between the elements of a stack. static let stack: CGFloat = 16 } } // MARK: - Previews #Preview( "Notification label style" ) { VStack( alignment: .leading, spacing: 16 ) { Label { Text(verbatim: "Downloading the speech model for Dutch (Netherlands)…") } icon: { Image(systemName: "arrow.down.circle") } .labelStyle(.notification( kind: .info )) Label { Text(verbatim: "The speech model download for Dutch (Netherlands) was cancelled.") } icon: { Image(systemName: "xmark.circle") } .labelStyle(.notification( kind: .warning )) Label { Text(verbatim: "The speech model for German (Germany) could not be downloaded.") } icon: { Image(systemName: "exclamationmark.triangle") } .labelStyle(.notification( kind: .error )) } }