Files
attendi/Widgets/Attendi/Sources/Styles/Buttons/ActionButtonStyle.swift
T

120 lines
3.5 KiB
Swift

import Commanding
import SwiftUI
import WidgetKit
/// The style of the action buttons of the recording Live Activity: it renders a button's label as a fixed-size icon on a circle
/// colored by the ``ActionType`` given at initialization.
///
/// The style is a regular ``ButtonStyle`` — not a ``PrimitiveButtonStyle`` — so that SwiftUI and WidgetKit keep handling the
/// button's tap and fire its intent from the Live Activity; a primitive style would have to invoke the trigger itself, which arbitrary
/// gestures cannot do inside a widget.
struct ActionButtonStyle: ButtonStyle {
// MARK: Properties
/// The type of the action behind the styled button, deciding the color of the circular background.
private let action: ActionType
// MARK: Initializers
/// Creates a style for a button behind the given type of action.
///
/// - Parameter action: The type of the action behind the styled button.
fileprivate init(
_ action: ActionType
) {
self.action = action
}
// MARK: Methods
/// Builds the styled button: its label as a fixed-size, padded icon on a circle colored by the type of the action.
///
/// - Parameter configuration: The properties of the button to style.
/// - Returns: The styled button.
func makeBody(
configuration: Configuration
) -> some View {
configuration.label
.scaledToFit()
.frame(
width: Constant.Size.icon.width,
height: Constant.Size.icon.height
)
.foregroundStyle(.primary)
.padding(Constant.Padding.icon)
.background {
Circle()
.foregroundStyle(backgroundColor)
}
}
}
// MARK: - Helpers
private extension ActionButtonStyle {
/// The color of the circular background, matching the type of the action behind the button.
var backgroundColor: Color {
switch action {
case .confirmative: .green
case let .custom(color): color
case .default: .yellow
case .destructive: .red
}
}
}
// MARK: - Enumerations
extension ActionButtonStyle {
/// The types of action a styled button can stand behind, each coloring the button's background differently.
enum ActionType {
/// An action that confirms or submits, colored green.
case confirmative
/// An action colored by the given custom color.
case custom(_ color: Color)
/// A regular action, colored yellow.
case `default`
/// An action that throws something away, colored red.
case destructive
}
}
// MARK: - Constants
/// The constant values used across the action button style.
private enum Constant {
/// The padding constants.
enum Padding {
/// The padding around the button's icon.
static let icon: CGFloat = 16
}
/// The size constants.
enum Size {
/// The size of the button's icon.
static let icon: CGSize = .init(
width: 24,
height: 24
)
}
}
// MARK: - Styles
extension ButtonStyle where Self == ActionButtonStyle {
/// An action button style for a button behind the given type of action.
///
/// - Parameter actionType: The type of the action behind the styled button.
/// - Returns: The action button style colored for the given type.
static func action(
type actionType: ActionButtonStyle.ActionType
) -> ActionButtonStyle {
.init(actionType)
}
}