143 lines
4.2 KiB
Swift
143 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
public struct AIButtonStyle: ButtonStyle {
|
|
public static let pressedScale: CGFloat = 0.97
|
|
|
|
public enum Kind: Hashable {
|
|
case primary
|
|
case secondary
|
|
case danger
|
|
|
|
var foregroundColor: Color { foregroundColor(isEnabled: true) }
|
|
|
|
var backgroundColor: Color { backgroundColor(isEnabled: true) }
|
|
|
|
func foregroundColor(isEnabled: Bool) -> Color {
|
|
switch self {
|
|
case .primary: return isEnabled ? AITheme.onPrimary : AITheme.muted
|
|
case .danger: return isEnabled ? AITheme.onDanger : AITheme.muted
|
|
case .secondary: return isEnabled ? AITheme.ink : AITheme.muted
|
|
}
|
|
}
|
|
|
|
func backgroundColor(isEnabled: Bool) -> Color {
|
|
switch self {
|
|
case .primary, .secondary, .danger:
|
|
return isEnabled ? kindBackground : AITheme.control
|
|
}
|
|
}
|
|
|
|
private var kindBackground: Color {
|
|
switch self {
|
|
case .primary: return AITheme.primary
|
|
case .secondary: return AITheme.control
|
|
case .danger: return AITheme.danger
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum Size: Hashable {
|
|
case compact
|
|
case regular
|
|
case large
|
|
case icon
|
|
case toolbar
|
|
case toolbarInline
|
|
}
|
|
|
|
private let kind: Kind
|
|
private let size: Size
|
|
private let fixedWidthRequest: CGFloat?
|
|
let fillsWidth: Bool
|
|
@Environment(\.isEnabled) private var isEnabled
|
|
|
|
public init(
|
|
kind: Kind = .secondary,
|
|
size: Size = .regular,
|
|
fixedWidth: CGFloat? = nil,
|
|
fillsWidth: Bool = false
|
|
) {
|
|
self.kind = kind
|
|
self.size = size
|
|
self.fixedWidthRequest = fixedWidth
|
|
self.fillsWidth = fillsWidth
|
|
}
|
|
|
|
public func makeBody(configuration: Configuration) -> some View {
|
|
let height: CGFloat =
|
|
switch size {
|
|
case .compact: AITheme.compactButtonHeight
|
|
case .large: AITheme.largeButtonHeight
|
|
case .icon: AITheme.iconButtonSize
|
|
case .regular: AITheme.buttonHeight
|
|
case .toolbar: AITheme.toolbarButtonHeight
|
|
case .toolbarInline: AITheme.toolbarButtonHeight
|
|
}
|
|
|
|
configuration.label
|
|
.labelStyle(.titleAndIcon)
|
|
.font(
|
|
.system(size: size == .compact || size == .toolbarInline ? 15 : 16, weight: .semibold)
|
|
)
|
|
.lineLimit(1)
|
|
.foregroundStyle(kind.foregroundColor(isEnabled: isEnabled))
|
|
.padding(.horizontal, fixedWidth == nil ? (kind == .secondary ? 12 : 14) : 0)
|
|
.frame(
|
|
minWidth: (!fillsWidth && fixedWidth == nil) ? minimumWidth : 0,
|
|
minHeight: height
|
|
)
|
|
.frame(maxWidth: fillsWidth ? .infinity : nil)
|
|
.frame(width: fixedWidth)
|
|
.background(
|
|
kind == .primary && isEnabled && size != .toolbarInline
|
|
? AnyShapeStyle(
|
|
LinearGradient(
|
|
colors: [AITheme.primaryTop, AITheme.primaryBottom],
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
)
|
|
: AnyShapeStyle(kind.backgroundColor(isEnabled: isEnabled))
|
|
)
|
|
.shadow(
|
|
color: (kind == .secondary || !isEnabled) ? .clear : .black.opacity(0.10),
|
|
radius: 12,
|
|
x: 0,
|
|
y: 5
|
|
)
|
|
.clipShape(
|
|
RoundedRectangle(
|
|
cornerRadius: size == .toolbarInline ? AITheme.toolbarButtonHeight / 2 : AITheme.buttonRadius,
|
|
style: .continuous
|
|
)
|
|
)
|
|
.overlay {
|
|
RoundedRectangle(
|
|
cornerRadius: size == .toolbarInline ? AITheme.toolbarButtonHeight / 2 : AITheme.buttonRadius,
|
|
style: .continuous
|
|
)
|
|
.strokeBorder(
|
|
size == .toolbarInline && kind == .primary
|
|
? AITheme.primary.opacity(0.28)
|
|
: (kind == .secondary || !isEnabled ? AITheme.line : .clear)
|
|
)
|
|
}
|
|
.scaleEffect(configuration.isPressed ? Self.pressedScale : 1)
|
|
.opacity(configuration.isPressed ? 0.82 : 1)
|
|
}
|
|
|
|
private var minimumWidth: CGFloat {
|
|
switch size {
|
|
case .compact: return AITheme.compactButtonMinWidth
|
|
case .regular: return AITheme.regularButtonMinWidth
|
|
case .large: return 92
|
|
case .icon: return AITheme.iconButtonSize
|
|
case .toolbar, .toolbarInline: return AITheme.toolbarButtonMinWidth
|
|
}
|
|
}
|
|
|
|
private var fixedWidth: CGFloat? {
|
|
fixedWidthRequest ?? (size == .icon ? AITheme.iconButtonSize : (size == .toolbar ? AITheme.toolbarButtonWidth : nil))
|
|
}
|
|
}
|