diff --git a/Sources/Luminare/Components/Auxiliary/Scroll Views/InfiniteScrollView.swift b/Sources/Luminare/Components/Auxiliary/Scroll Views/InfiniteScrollView.swift index c1f2d63..7c00c0f 100644 --- a/Sources/Luminare/Components/Auxiliary/Scroll Views/InfiniteScrollView.swift +++ b/Sources/Luminare/Components/Auxiliary/Scroll Views/InfiniteScrollView.swift @@ -9,13 +9,13 @@ import AppKit import SwiftUI /// The direction of an ``InfiniteScrollView``. -public enum InfiniteScrollViewDirection: String, Equatable, Hashable, Identifiable, CaseIterable, Codable { +public enum InfiniteScrollViewDirection: String, Equatable, Hashable, Identifiable, CaseIterable, Codable, Sendable { /// The view can, and can only be scrolled horizontally. case horizontal /// The view can, and can only be scrolled vertically. case vertical - public var id: String { rawValue } + public var id: Self { self } /// Initializes an ``InfiniteScrollViewDirection`` from an `Axis`. public init(axis: Axis) { diff --git a/Sources/Luminare/Components/Compose/LuminareButtonCompose.swift b/Sources/Luminare/Components/Compose/LuminareButtonCompose.swift new file mode 100644 index 0000000..70a82e9 --- /dev/null +++ b/Sources/Luminare/Components/Compose/LuminareButtonCompose.swift @@ -0,0 +1,112 @@ +// +// LuminareButtonCompose.swift +// Luminare +// +// Created by KrLite on 2024/12/17. +// + +import SwiftUI + +// MARK: - Button Compose + +struct LuminareButtonCompose: View where Label: View, Content: View { + // MARK: Fields + + private let role: ButtonRole? + @ViewBuilder private var label: () -> Label + @ViewBuilder private var content: () -> Content + private let action: () -> () + + // MARK: Initializers + + public init( + role: ButtonRole? = nil, + @ViewBuilder label: @escaping () -> Label, + @ViewBuilder content: @escaping () -> Content, + action: @escaping () -> () + ) { + self.role = role + self.label = label + self.content = content + self.action = action + } + + public init( + _ titleKey: LocalizedStringKey, + role: ButtonRole? = nil, + @ViewBuilder content: @escaping () -> Content, + action: @escaping () -> () + ) where Label == Text { + self.init( + role: role + ) { + Text(titleKey) + } content: { + content() + } action: { + action() + } + } + + public init( + _ contentKey: LocalizedStringKey, + role: ButtonRole? = nil, + @ViewBuilder label: @escaping () -> Label, + action: @escaping () -> () + ) where Content == Text { + self.init( + role: role, + label: label + ) { + Text(contentKey) + } action: { + action() + } + } + + public init( + _ titleKey: LocalizedStringKey, + _ contentKey: LocalizedStringKey, + role: ButtonRole? = nil, + action: @escaping () -> () + ) where Label == Text, Content == Text { + self.init( + role: role + ) { + Text(titleKey) + } content: { + Text(contentKey) + } action: { + action() + } + } + + // MARK: Body + + var body: some View { + LuminareCompose(contentMaxWidth: nil) { + Button(role: role) { + action() + } label: { + label() + } + .buttonStyle(.luminareCompact) + } label: { + label() + } + } +} + +// MARK: - Preview + +@available(macOS 15.0, *) +#Preview( + "LuminareButtonCompose", + traits: .sizeThatFitsLayout +) { + LuminareSection { + LuminareButtonCompose("Button", "Click Me!") { + print(1) + } + } +} diff --git a/Sources/Luminare/Components/Compose/LuminareCompose.swift b/Sources/Luminare/Components/Compose/LuminareCompose.swift index 0b12b72..eab255b 100644 --- a/Sources/Luminare/Components/Compose/LuminareCompose.swift +++ b/Sources/Luminare/Components/Compose/LuminareCompose.swift @@ -7,18 +7,59 @@ import SwiftUI -/// The control size for views based on ``LuminareCompose``. +public enum LuminareComposeControlSize: String, Equatable, Hashable, Identifiable, CaseIterable, Codable, Sendable { + case automatic + @available(macOS 14.0, *) + case extraLarge + case large + case regular + case small + case mini + + public static var allCases: [LuminareComposeControlSize] { + if #available(macOS 14.0, *) { + [.automatic, .extraLarge, .large, .regular, .small, .mini] + } else { + [.automatic, .large, .regular, .small, .mini] + } + } + + public var id: Self { self } + + public var proposal: ControlSize? { + if #available(macOS 14.0, *) { + switch self { + case .extraLarge: .extraLarge + case .large: .large + case .regular: .regular + case .small: .small + case .mini: .mini + default: nil + } + } else { + switch self { + case .large: .large + case .regular: .regular + case .small: .small + case .mini: .mini + default: nil + } + } + } +} + +/// The layout for views based on ``LuminareCompose``. /// /// Typically, this is eligible for views that have additional controls beside static contents. -public enum LuminareComposeControlSize: String, Equatable, Hashable, Identifiable, CaseIterable, Codable { - /// The regular size where the content is separated into two lines. +public enum LuminareComposeLayout: String, Equatable, Hashable, Identifiable, CaseIterable, Codable, Sendable { + /// The regular layout where the content is separated into two lines. case regular - /// The compact size where the content is in one single line. + /// The compact layout where the content is in one single line. case compact - public var id: String { rawValue } + public var id: Self { self } - var height: CGFloat { + public var height: CGFloat { switch self { case .regular: 70 case .compact: 34 @@ -26,12 +67,12 @@ public enum LuminareComposeControlSize: String, Equatable, Hashable, Identifiabl } } -public enum LuminareComposeStyle: String, Equatable, Hashable, Identifiable, CaseIterable, Codable { +public enum LuminareComposeStyle: String, Equatable, Hashable, Identifiable, CaseIterable, Codable, Sendable { case automatic case regular case inline - public var id: String { rawValue } + public var id: Self { self } } // MARK: - Compose @@ -44,6 +85,7 @@ public struct LuminareCompose: View @Environment(\.isEnabled) private var isEnabled @Environment(\.luminareMinHeight) private var minHeight @Environment(\.luminareHorizontalPadding) private var horizontalPadding + @Environment(\.luminareComposeControlSize) private var controlSize @Environment(\.luminareComposeStyle) private var style // MARK: Fields @@ -91,10 +133,9 @@ public struct LuminareCompose: View ) where Label == Text { self.init( contentMaxWidth: contentMaxWidth, - spacing: spacing + spacing: spacing, + content: content ) { - content() - } label: { Text(key) } } @@ -116,11 +157,11 @@ public struct LuminareCompose: View HStack(spacing: 0) { Spacer() - content() + wrappedContent() } .frame(maxWidth: contentMaxWidth) } else { - content() + wrappedContent() } } .frame(maxWidth: .infinity, minHeight: minHeight) @@ -153,6 +194,15 @@ public struct LuminareCompose: View default: .init(top: 0, leading: horizontalPadding, bottom: 0, trailing: horizontalPadding) } } + + @ViewBuilder private func wrappedContent() -> some View { + if let controlSize = controlSize.proposal { + content() + .controlSize(controlSize) + } else { + content() + } + } } // MARK: - Preview diff --git a/Sources/Luminare/Components/Compose/LuminareSliderPickerCompose.swift b/Sources/Luminare/Components/Compose/LuminareSliderPickerCompose.swift index 3f55ac6..0a71758 100644 --- a/Sources/Luminare/Components/Compose/LuminareSliderPickerCompose.swift +++ b/Sources/Luminare/Components/Compose/LuminareSliderPickerCompose.swift @@ -15,7 +15,7 @@ public struct LuminareSliderPickerCompose: View where Label: @Environment(\.luminareAnimation) private var animation @Environment(\.luminareHorizontalPadding) private var horizontalPadding - @Environment(\.luminareComposeControlSize) private var controlSize + @Environment(\.luminareComposeLayout) private var layout // MARK: Fields @@ -114,7 +114,7 @@ public struct LuminareSliderPickerCompose: View where Label: public var body: some View { VStack { - switch controlSize { + switch layout { case .regular: LuminareCompose { text() @@ -139,7 +139,7 @@ public struct LuminareSliderPickerCompose: View where Label: .luminareComposeStyle(.inline) } } - .frame(height: controlSize.height) + .frame(height: layout.height) .animation(animation, value: selection) } @@ -217,6 +217,6 @@ public struct LuminareSliderPickerCompose: View where Label: .foregroundStyle(.secondary) } } - .luminareComposeControlSize(.compact) + .luminareComposeLayout(.compact) } } diff --git a/Sources/Luminare/Components/Compose/LuminareToggleCompose.swift b/Sources/Luminare/Components/Compose/LuminareToggleCompose.swift new file mode 100644 index 0000000..56b3055 --- /dev/null +++ b/Sources/Luminare/Components/Compose/LuminareToggleCompose.swift @@ -0,0 +1,67 @@ +// +// LuminareToggleCompose.swift +// Luminare +// +// Created by KrLite on 2024/12/17. +// + +import SwiftUI + +// MARK: - Toggle Compose + +struct LuminareToggleCompose