From a65ac76dde388ed245432849570f94e9e66dda00 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 4 Jun 2024 19:38:40 -0400 Subject: [PATCH] feat: Adds more buttons and reverse label style --- .../EstimateSettingsForm.swift | 6 +- Sources/Styleguide/Buttons.swift | 40 ++++++++++- Sources/Styleguide/InfoView.swift | 4 +- Sources/Styleguide/Styles/ButtonStyles.swift | 72 ++++++++++++++++--- .../Styleguide/Styles/ReverseLabelStyle.swift | 24 +++++++ 5 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 Sources/Styleguide/Styles/ReverseLabelStyle.swift diff --git a/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift b/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift index 1f274ba..5e70283 100644 --- a/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift +++ b/Sources/PressureEstimationsFeature/EstimateSettingsForm.swift @@ -221,10 +221,8 @@ public struct EstimateSettingsFormView: View { HStack { ResetButton { send(.resetButtonTapped) } Spacer() - Button("Next") { - send(.nextButtonTapped) - } - .disabled(!store.isValid) + NextButton { send(.nextButtonTapped) } + .disabled(!store.isValid) } .padding(.top) } diff --git a/Sources/Styleguide/Buttons.swift b/Sources/Styleguide/Buttons.swift index 15d08f6..a5f269f 100644 --- a/Sources/Styleguide/Buttons.swift +++ b/Sources/Styleguide/Buttons.swift @@ -1,5 +1,20 @@ import SwiftUI +public struct DoneButton: View { + + let action: () -> Void + + public init(action: @escaping () -> Void) { + self.action = action + } + + public var body: some View { + Button(action: action) { + Text("Done") + } + } +} + public struct InfoButton: View { @Environment(\.infoButtonStyle) private var infoButtonStyle @@ -18,6 +33,26 @@ public struct InfoButton: View { } } +public struct NextButton: View { + + @Environment(\.nextButtonStyle) private var nextButtonStyle + + let action: () -> Void + + public init(action: @escaping () -> Void) { + self.action = action + } + + public var body: some View { + Button { + action() + } label: { + Label("Next", systemImage: "chevron.right") + } + .buttonStyle(nextButtonStyle) + } +} + public struct ResetButton: View { @Environment(\.resetButtonStyle) private var resetButtonStyle @@ -48,7 +83,10 @@ struct ButtonPreview: View { InfoButton { lastButtonPressed = "Info button pressed." } - ResetButton { + NextButton { + lastButtonPressed = "Next button pressed." + } + ResetButton { lastButtonPressed = "Reset button pressed." } } diff --git a/Sources/Styleguide/InfoView.swift b/Sources/Styleguide/InfoView.swift index 7c36090..87f797b 100644 --- a/Sources/Styleguide/InfoView.swift +++ b/Sources/Styleguide/InfoView.swift @@ -54,9 +54,7 @@ public struct InfoView: View { .padding(.horizontal) .navigationBarBackButtonHidden() .toolbar { - Button("Done") { - store.send(.dismissButtonTapped) - } + DoneButton { store.send(.dismissButtonTapped) } } } } diff --git a/Sources/Styleguide/Styles/ButtonStyles.swift b/Sources/Styleguide/Styles/ButtonStyles.swift index 3c3e544..9339c08 100644 --- a/Sources/Styleguide/Styles/ButtonStyles.swift +++ b/Sources/Styleguide/Styles/ButtonStyles.swift @@ -3,9 +3,13 @@ import SwiftUI /// A name space for info button styles. public enum InfoButtonType { } +/// A name space for info button styles. +public enum NextButtonType { } + /// A name space for info button styles. public enum ResetButtonType { } +/// A type erased button style, used to style buttons in a namespace. public struct AnyButtonStyle: ButtonStyle { private let _makeBody: (Configuration) -> AnyView @@ -20,6 +24,7 @@ public struct AnyButtonStyle: ButtonStyle { } } +/// A type erased primitive button style, used to style buttons in a namespace. public struct AnyPrimitiveButtonStyle: PrimitiveButtonStyle { private let _makeBody: (Configuration) -> AnyView @@ -34,36 +39,62 @@ public struct AnyPrimitiveButtonStyle: PrimitiveButtonStyle { } } - - +/// The default info button style. public struct DefaultInfoButtonStyle: ButtonStyle { let color: Color + let font: Font let labelStyle: Style init( color: Color = .accentColor, + font: Font = .title2, labelStyle: Style ) { self.color = color + self.font = font self.labelStyle = labelStyle } public func makeBody(configuration: Configuration) -> some View { configuration.label - .font(.title2) + .font(font) .foregroundStyle(color.opacity(configuration.isPressed ? 0.5 : 1)) .labelStyle(labelStyle) .scaleEffect(configuration.isPressed ? 0.8 : 1) } } +public struct DefaultNextButtonStyle: PrimitiveButtonStyle { + let buttonStyle: ButtonStyle + let labelStyle: Label + + public init(buttonStyle: ButtonStyle, labelStyle: Label) { + self.buttonStyle = buttonStyle + self.labelStyle = labelStyle + } + + public func makeBody(configuration: Configuration) -> some View { + Button(role: configuration.role, action: configuration.trigger) { + configuration.label + } + .labelStyle(labelStyle) + .buttonStyle(buttonStyle) + } +} + +extension DefaultNextButtonStyle where ButtonStyle == BorderedProminentButtonStyle, Label == ReverseLabelStyle { + init() { + self.init(buttonStyle: .borderedProminent, labelStyle: .reverse()) + } +} + extension AnyButtonStyle where ButtonType == InfoButtonType { public static var `default`: Self { .init(DefaultInfoButtonStyle(labelStyle: .iconOnly)) } - public static func `default`(color: Color, labelStyle: S) -> Self { - .init(DefaultInfoButtonStyle(color: color, labelStyle: labelStyle)) + public static func `default`(color: Color, font: Font, labelStyle: S) -> Self { + .init(DefaultInfoButtonStyle(color: color, font: font, labelStyle: labelStyle)) } } @@ -71,17 +102,28 @@ private struct InfoButtonStyleKey: EnvironmentKey { static var defaultValue = AnyButtonStyle.default } +private struct NextButtonStyleKey: EnvironmentKey { + static var defaultValue = AnyPrimitiveButtonStyle( + DefaultNextButtonStyle() + ) +} + private struct ResetButtonStyleKey: EnvironmentKey { - static var defaultValue = AnyPrimitiveButtonStyle(BorderedProminentButtonStyle()) + static var defaultValue = AnyPrimitiveButtonStyle(.borderedProminent) } extension EnvironmentValues { - public var infoButtonStyle: AnyButtonStyle { + var infoButtonStyle: AnyButtonStyle { get { self[InfoButtonStyleKey.self] } set { self[InfoButtonStyleKey.self] = newValue } } - public var resetButtonStyle: AnyPrimitiveButtonStyle { + var nextButtonStyle: AnyPrimitiveButtonStyle { + get { self[NextButtonStyleKey.self] } + set { self[NextButtonStyleKey.self] = newValue } + } + + var resetButtonStyle: AnyPrimitiveButtonStyle { get { self[ResetButtonStyleKey.self] } set { self[ResetButtonStyleKey.self] = newValue } } @@ -89,18 +131,32 @@ extension EnvironmentValues { extension View { + /// Sets the button style for the ``InfoButton`` type. public func infoButtonStyle(_ style: AnyButtonStyle) -> some View { environment(\.infoButtonStyle, style) } + /// Sets the button style for the ``InfoButton`` type. public func infoButtonStyle(_ style: S) -> some View { infoButtonStyle(AnyButtonStyle(style)) } + /// Sets the button style for the ``NextButton`` type. + public func nextButtonStyle(_ style: AnyPrimitiveButtonStyle) -> some View { + environment(\.nextButtonStyle, style) + } + + /// Sets the button style for the ``NextButton`` type. + public func nextButtonStyle(_ style: S) -> some View { + nextButtonStyle(AnyPrimitiveButtonStyle(style)) + } + + /// Sets the button style for the ``ResetButton`` type. public func resetButtonStyle(_ style: AnyPrimitiveButtonStyle) -> some View { environment(\.resetButtonStyle, style) } + /// Sets the button style for the ``ResetButton`` type. public func resetButtonStyle(_ style: S) -> some View { resetButtonStyle(AnyPrimitiveButtonStyle(style)) } diff --git a/Sources/Styleguide/Styles/ReverseLabelStyle.swift b/Sources/Styleguide/Styles/ReverseLabelStyle.swift new file mode 100644 index 0000000..4e37734 --- /dev/null +++ b/Sources/Styleguide/Styles/ReverseLabelStyle.swift @@ -0,0 +1,24 @@ +import SwiftUI + +/// A label style that puts the title first and icon second. +public struct ReverseLabelStyle: LabelStyle { + let spacing: CGFloat + + public init(spacing: CGFloat = 3) { + self.spacing = spacing + } + + public func makeBody(configuration: Configuration) -> some View { + HStack(spacing: spacing) { + configuration.title + configuration.icon + } + } +} + +extension LabelStyle where Self == ReverseLabelStyle { + + public static func reverse(spacing: CGFloat = 3) -> Self { + .init(spacing: spacing) + } +}