diff --git a/Sources/OversizeUI/Controls/HUD/HUD.swift b/Sources/OversizeUI/Controls/HUD/HUD.swift
index eff6dc4..ad1248c 100644
--- a/Sources/OversizeUI/Controls/HUD/HUD.swift
+++ b/Sources/OversizeUI/Controls/HUD/HUD.swift
@@ -14,8 +14,12 @@ public struct HUD
: View where Title: View, Icon: View {
private let isAutoHide: Bool
@Binding private var isPresented: Bool
+ #if os(macOS)
+ @State private var offset: CGFloat = 200
+ #else
+ @State private var offset: CGFloat = -200
+ #endif
- @State private var bottomOffset: CGFloat = 0
@State private var opacity: CGFloat = 0
// MARK: Initializers
@@ -41,7 +45,12 @@ public struct HUD: View where Title: View, Icon: View {
if let text {
Text(text)
.body(.medium)
- .foregroundColor(.onSurfaceHighEmphasis)
+ #if os(macOS)
+ .foregroundColor(Color.onPrimaryHighEmphasis)
+ #else
+ .foregroundColor(Color.onSurfaceHighEmphasis)
+
+ #endif
} else if let title {
title
@@ -50,26 +59,41 @@ public struct HUD: View where Title: View, Icon: View {
.padding(.leading, icon == nil ? .medium : .small)
.padding(.trailing, .medium)
.padding(.vertical, .xSmall)
- .background(
- Capsule()
- .foregroundColor(Color.surfacePrimary)
- .shadowElevaton(.z2)
- )
- .padding(.small)
- .opacity(opacity)
- .offset(y: bottomOffset)
- .onChange(of: isPresented, perform: { present in
- if present {
- presentAnimated()
- } else {
- dismissAnimated()
+ #if os(macOS)
+ .background(
+ RoundedRectangle(cornerRadius: .small, style: .continuous)
+ .foregroundColor(Color.onBackgroundHighEmphasis)
+ .shadowElevaton(.z2)
+ )
+ #else
+ .background(
+ Capsule()
+ .foregroundColor(Color.surfacePrimary)
+ .shadowElevaton(.z2)
+ )
+ #endif
+ .padding(.small)
+ .opacity(opacity)
+ .offset(y: offset)
+ .onChange(of: isPresented) { present in
+ if present {
+ if offset == 0 {
+ dismissAnimated()
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ presentAnimated()
+ }
+ } else {
+ presentAnimated()
+ }
+ } else {
+ dismissAnimated()
+ }
}
- })
}
private func presentAnimated() {
withAnimation {
- bottomOffset = 0
+ offset = 0
opacity = 1
}
if isAutoHide {
@@ -83,7 +107,11 @@ public struct HUD: View where Title: View, Icon: View {
private func dismissAnimated() {
withAnimation {
- bottomOffset = -200
+ #if os(macOS)
+ offset = 200
+ #else
+ offset = -200
+ #endif
opacity = 0
}
}
@@ -132,6 +160,42 @@ public extension HUD where Icon == EmptyView {
}
}
+#if os(macOS)
+@MainActor
+public extension View {
+ func hud(_ text: String, autoHide: Bool = true, isPresented: Binding) -> some View {
+ overlay(alignment: .bottomTrailing) {
+ HUD(text, autoHide: autoHide, isPresented: isPresented)
+ }
+ }
+
+ func hud(_ text: String, isPresented: Binding, @ViewBuilder icon: () -> some View) -> some View {
+ overlay(alignment: .bottomTrailing) {
+ HUD(text, isPresented: isPresented, icon: icon)
+ }
+ }
+
+ func hud(isPresented: Binding, @ViewBuilder title: () -> some View) -> some View {
+ overlay(alignment: .bottomTrailing) {
+ HUD(isPresented: isPresented, title: title)
+ }
+ }
+
+ func hud(isPresented: Binding, @ViewBuilder title: () -> some View, @ViewBuilder icon: () -> some View) -> some View {
+ overlay(alignment: .bottomTrailing) {
+ HUD(isPresented: isPresented, title: title, icon: icon)
+ }
+ }
+
+ func hudLoader(_ text: String = "Loading", isPresented: Binding) -> some View {
+ overlay(alignment: .bottomTrailing) {
+ HUD(text, autoHide: false, isPresented: isPresented) {
+ ProgressView()
+ }
+ }
+ }
+}
+#else
public extension View {
func hud(_ text: String, isPresented: Binding) -> some View {
overlay(alignment: .top) {
@@ -165,3 +229,4 @@ public extension View {
}
}
}
+#endif
diff --git a/Sources/OversizeUI/Controls/PageView/Page.swift b/Sources/OversizeUI/Controls/PageView/Page.swift
index b176af8..f5025e5 100644
--- a/Sources/OversizeUI/Controls/PageView/Page.swift
+++ b/Sources/OversizeUI/Controls/PageView/Page.swift
@@ -4,6 +4,8 @@ import SwiftUI
public struct Page: View
where Content: View, Header: View, LeadingBar: View, TrailingBar: View, TopToolbar: View, TitleLabel: View
{
+ @Environment(\.platform) var platform
+
public typealias ScrollAction = (_ offset: CGPoint, _ headerVisibleRatio: CGFloat) -> Void
private let title: String?
@@ -103,7 +105,7 @@ public struct Page CGFloat) -> some View
+ computeValue: @Sendable @escaping (ViewDimensions) -> CGFloat) -> some View
{
if isActive {
alignmentGuide(alignment, computeValue: computeValue)
@@ -33,7 +33,7 @@ extension View {
@ViewBuilder
@inlinable func alignmentGuide(_ alignment: VerticalAlignment,
isActive: Bool,
- computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View
+ computeValue: @Sendable @escaping (ViewDimensions) -> CGFloat) -> some View
{
if isActive {
alignmentGuide(alignment, computeValue: computeValue)
diff --git a/Sources/OversizeUI/Controls/Stacks/LeadingVStack.swift b/Sources/OversizeUI/Controls/Stacks/LeadingVStack.swift
new file mode 100644
index 0000000..f2a9293
--- /dev/null
+++ b/Sources/OversizeUI/Controls/Stacks/LeadingVStack.swift
@@ -0,0 +1,102 @@
+//
+// Copyright © 2024 Alexander Romanov
+// LeadingVStack.swift, created on 28.10.2024
+//
+
+import SwiftUI
+
+public struct LeadingVStack: View {
+ private let spacing: Space
+ private let content: Content
+
+ public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) {
+ self.spacing = spacing
+ self.content = content()
+ }
+
+ public var body: some View {
+ VStack(alignment: .leading, spacing: spacing.rawValue) {
+ content
+ }
+ }
+}
+
+public struct TrailingVStack: View {
+ private let spacing: Space
+ private let content: Content
+
+ public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) {
+ self.spacing = spacing
+ self.content = content()
+ }
+
+ public var body: some View {
+ VStack(alignment: .trailing, spacing: spacing.rawValue) {
+ content
+ }
+ }
+}
+
+public struct CenterVStack: View {
+ private let spacing: Space
+ private let content: Content
+
+ public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) {
+ self.spacing = spacing
+ self.content = content()
+ }
+
+ public var body: some View {
+ VStack(alignment: .center, spacing: spacing.rawValue) {
+ content
+ }
+ }
+}
+
+public struct LeadingLazyVStack: View {
+ private let spacing: Space
+ private let content: Content
+
+ public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) {
+ self.spacing = spacing
+ self.content = content()
+ }
+
+ public var body: some View {
+ LazyVStack(alignment: .leading, spacing: spacing.rawValue) {
+ content
+ }
+ }
+}
+
+public struct TrailingLazyVStack: View {
+ private let spacing: Space
+ private let content: Content
+
+ public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) {
+ self.spacing = spacing
+ self.content = content()
+ }
+
+ public var body: some View {
+ LazyVStack(alignment: .trailing, spacing: spacing.rawValue) {
+ content
+ }
+ }
+}
+
+public struct CenterLazyVStack: View {
+ private let spacing: Space
+ private let content: Content
+
+ public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) {
+ self.spacing = spacing
+ self.content = content()
+ }
+
+ public var body: some View {
+ LazyVStack(alignment: .center, spacing: spacing.rawValue) {
+ content
+ }
+ }
+}
diff --git a/Sources/OversizeUI/Controls/Surface/Surface.swift b/Sources/OversizeUI/Controls/Surface/Surface.swift
index cd13ba9..9fade58 100644
--- a/Sources/OversizeUI/Controls/Surface/Surface.swift
+++ b/Sources/OversizeUI/Controls/Surface/Surface.swift
@@ -36,6 +36,8 @@ public struct Surface: View {
private let forceContentInsets: EdgeSpaceInsets?
private var isSurfaceClipped: Bool = false
+ @State var isHover = false
+
public init(
action: (() -> Void)? = nil,
@ViewBuilder label: () -> Label
@@ -58,8 +60,12 @@ public struct Surface: View {
action?()
} label: {
surface
+ .contentShape(Rectangle())
}
.buttonStyle(SurfaceButtonStyle())
+ .onHover { hover in
+ isHover = hover
+ }
}
private var surface: some View {
@@ -68,14 +74,33 @@ public struct Surface: View {
.padding(.bottom, forceContentInsets?.bottom ?? contentInsets.bottom)
.padding(.leading, forceContentInsets?.leading ?? contentInsets.leading)
.padding(.trailing, forceContentInsets?.trailing ?? contentInsets.trailing)
- .background(
+ .background {
+ #if os(macOS)
+ ZStack {
+ RoundedRectangle(
+ cornerRadius: surfaceRadius,
+ style: .continuous
+ )
+ .fill(surfaceBackgroundColor)
+ .shadowElevaton(elevation)
+
+ if isHover {
+ RoundedRectangle(
+ cornerRadius: surfaceRadius,
+ style: .continuous
+ )
+ .fill(Color.onSurfaceDisabled.opacity(0.04))
+ }
+ }
+ #else
RoundedRectangle(
cornerRadius: surfaceRadius,
style: .continuous
)
.fill(surfaceBackgroundColor)
.shadowElevaton(elevation)
- )
+ #endif
+ }
.overlay(
RoundedRectangle(
cornerRadius: surfaceRadius,
@@ -171,7 +196,11 @@ public struct SurfaceButtonStyle: ButtonStyle {
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
+ #if os(macOS)
+ .scaleEffect(configuration.isPressed ? 0.98 : 1)
+ #else
.scaleEffect(configuration.isPressed ? 0.96 : 1)
+ #endif
}
}
diff --git a/Sources/OversizeUI/Controls/Toast/Snackbar.swift b/Sources/OversizeUI/Controls/Toast/Snackbar.swift
index ffa46d4..3e04082 100644
--- a/Sources/OversizeUI/Controls/Toast/Snackbar.swift
+++ b/Sources/OversizeUI/Controls/Toast/Snackbar.swift
@@ -55,7 +55,6 @@ public struct Snackbar