mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-11 12:15:53 +00:00
77 lines
2.7 KiB
Swift
77 lines
2.7 KiB
Swift
import SwiftUI
|
|
|
|
private let logger = Logger(category: "ScrollToBottomButton")
|
|
|
|
/// Floating action button to scroll terminal to bottom
|
|
struct ScrollToBottomButton: View {
|
|
let isVisible: Bool
|
|
let action: () -> Void
|
|
@State private var isHovered = false
|
|
@State private var isPressed = false
|
|
|
|
var body: some View {
|
|
Button(action: {
|
|
HapticFeedback.impact(.light)
|
|
action()
|
|
}, label: {
|
|
Text("↓")
|
|
.font(.system(size: 24, weight: .bold))
|
|
.foregroundColor(isHovered ? Theme.Colors.primaryAccent : Theme.Colors.terminalForeground)
|
|
.frame(width: 48, height: 48)
|
|
.background(
|
|
Circle()
|
|
.fill(isHovered ? Theme.Colors.cardBackground : Theme.Colors.cardBackground.opacity(0.8))
|
|
.overlay(
|
|
Circle()
|
|
.stroke(
|
|
isHovered ? Theme.Colors.primaryAccent : Theme.Colors.cardBorder,
|
|
lineWidth: isHovered ? 2 : 1
|
|
)
|
|
)
|
|
)
|
|
.shadow(
|
|
color: isHovered ? Theme.Colors.primaryAccent.opacity(0.3) : .black.opacity(0.3),
|
|
radius: isHovered ? 12 : 8,
|
|
x: 0,
|
|
y: isHovered ? 3 : 4
|
|
)
|
|
.scaleEffect(isPressed ? 0.95 : 1.0)
|
|
.offset(y: isHovered && !isPressed ? -1 : 0)
|
|
})
|
|
.buttonStyle(PlainButtonStyle())
|
|
.opacity(isVisible ? 1 : 0)
|
|
.scaleEffect(isVisible ? 1 : 0.8)
|
|
.animation(Theme.Animation.quick, value: isHovered)
|
|
.animation(Theme.Animation.quick, value: isPressed)
|
|
.animation(Theme.Animation.smooth, value: isVisible)
|
|
.allowsHitTesting(isVisible)
|
|
.onLongPressGesture(minimumDuration: 0, maximumDistance: .infinity) { pressing in
|
|
isPressed = pressing
|
|
} perform: {
|
|
// Action handled by button
|
|
}
|
|
.onHover { hovering in
|
|
isHovered = hovering
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: Use ScrollToBottomButton directly with overlay instead of this extension
|
|
// Example:
|
|
// .overlay(
|
|
// ScrollToBottomButton(isVisible: showButton, action: { })
|
|
// .padding(.bottom, Theme.Spacing.large)
|
|
// .padding(.leading, Theme.Spacing.large),
|
|
// alignment: .bottomLeading
|
|
// )
|
|
|
|
#Preview {
|
|
ZStack {
|
|
Theme.Colors.terminalBackground
|
|
.ignoresSafeArea()
|
|
|
|
ScrollToBottomButton(isVisible: true) {
|
|
logger.debug("Scroll to bottom")
|
|
}
|
|
}
|
|
}
|