From 8f4577c036e58501ea6f9758716f1ba8a2b66aaf Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:16:53 +0800 Subject: [PATCH 01/23] LuminareButtonCompose --- .../Composes/LuminareButtonCompose.swift | 94 +++++++++++++++++++ .../EnvironmentValues+Extensions.swift | 4 + 2 files changed, 98 insertions(+) create mode 100644 Sources/Luminare/Components/Composes/LuminareButtonCompose.swift diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift new file mode 100644 index 00000000..2dc665aa --- /dev/null +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -0,0 +1,94 @@ +// +// LuminareButtonCompose.swift +// Luminare +// +// Created by Adon Omeri on 23/3/2026. +// + +import SwiftUI + +@resultBuilder +public struct LuminareButtonBuilder { + public static func buildBlock(_ components: any View...) -> [any View] { + components + } +} + +public enum PositionInList { + case top, middle, bottom, unknown +} + +public struct LuminareButtonCompose: View { + @Environment(\.luminareButtonComposeSpacing) private var spacing + + private let buttons: [any View] + private let positionInList: PositionInList + + public init( + _ positionInList: PositionInList = .unknown, + @LuminareButtonBuilder _ buttons: () -> [any View] + ) { + self.positionInList = positionInList + self.buttons = buttons() + } + + public var body: some View { + HStack(spacing: spacing) { + ForEach(Array(buttons.enumerated()), id: \.offset) { index, button in + let isFirst = index == 0 + let isLast = index == buttons.count - 1 + + AnyView( + button + .luminareRoundingBehavior( + topLeading: positionInList == .middle || positionInList == .bottom ? false : + positionInList == .unknown ? true : + isFirst ? true : false, + topTrailing: positionInList == .middle || positionInList == .bottom ? false : + positionInList == .unknown ? true : + isLast ? true : false, + bottomLeading: positionInList == .middle || positionInList == .top ? false : + positionInList == .unknown ? true : + isFirst ? true : false, + bottomTrailing: positionInList == .middle || positionInList == .top ? false : + positionInList == .unknown ? true : + isLast ? true : false + ) + ) + } + } + .buttonStyle(.luminare) + } +} + +@available(macOS 15.0, *) +#Preview( + "LuminareButton", + traits: .sizeThatFitsLayout +) { + LuminarePane { + LuminareSection { + Text("LuminareButtonCompose") + + LuminareButtonCompose(.bottom) { + Button { + print(1) + } label: { + Text("Button 1") + } + + Button { + print(1) + } label: { + Text("Button 1") + } + + Button { + print(2) + } label: { + Text("Button 2") + } + } + } + } +} diff --git a/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift b/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift index bcfbc486..f65f2051 100644 --- a/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift @@ -91,6 +91,10 @@ public extension EnvironmentValues { /// If 0, then luminareSection will be of fixed size. @Entry var luminareSectionMaxWidth: CGFloat? = .infinity + // MARK: ButtonCompose + + @Entry var luminareButtonComposeSpacing: CGFloat = 4 + // MARK: Compose @Entry var luminareComposeControlSize: LuminareComposeControlSize = .automatic From 90b715c65a05fa74517b64d49d5f6f0567148a9d Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:42:28 +0800 Subject: [PATCH 02/23] improve modifier logic --- .../Composes/LuminareButtonCompose.swift | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 2dc665aa..36e78921 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -37,22 +37,17 @@ public struct LuminareButtonCompose: View { ForEach(Array(buttons.enumerated()), id: \.offset) { index, button in let isFirst = index == 0 let isLast = index == buttons.count - 1 - + + let roundTop = positionInList == .unknown || positionInList == .top + let roundBottom = positionInList == .unknown || positionInList == .bottom + AnyView( button .luminareRoundingBehavior( - topLeading: positionInList == .middle || positionInList == .bottom ? false : - positionInList == .unknown ? true : - isFirst ? true : false, - topTrailing: positionInList == .middle || positionInList == .bottom ? false : - positionInList == .unknown ? true : - isLast ? true : false, - bottomLeading: positionInList == .middle || positionInList == .top ? false : - positionInList == .unknown ? true : - isFirst ? true : false, - bottomTrailing: positionInList == .middle || positionInList == .top ? false : - positionInList == .unknown ? true : - isLast ? true : false + topLeading: roundTop && isFirst, + topTrailing: roundTop && isLast, + bottomLeading: roundBottom && isFirst, + bottomTrailing: roundBottom && isLast ) ) } From bcb626329b15f98cd037f30bbfc8156ac8a61a18 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:45:05 +0800 Subject: [PATCH 03/23] fix formatting --- .../Composes/LuminareButtonCompose.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 36e78921..4d8a01ee 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -20,10 +20,10 @@ public enum PositionInList { public struct LuminareButtonCompose: View { @Environment(\.luminareButtonComposeSpacing) private var spacing - + private let buttons: [any View] private let positionInList: PositionInList - + public init( _ positionInList: PositionInList = .unknown, @LuminareButtonBuilder _ buttons: () -> [any View] @@ -31,22 +31,22 @@ public struct LuminareButtonCompose: View { self.positionInList = positionInList self.buttons = buttons() } - + public var body: some View { HStack(spacing: spacing) { ForEach(Array(buttons.enumerated()), id: \.offset) { index, button in let isFirst = index == 0 let isLast = index == buttons.count - 1 - let roundTop = positionInList == .unknown || positionInList == .top + let roundTop = positionInList == .unknown || positionInList == .top let roundBottom = positionInList == .unknown || positionInList == .bottom AnyView( button .luminareRoundingBehavior( - topLeading: roundTop && isFirst, - topTrailing: roundTop && isLast, - bottomLeading: roundBottom && isFirst, + topLeading: roundTop && isFirst, + topTrailing: roundTop && isLast, + bottomLeading: roundBottom && isFirst, bottomTrailing: roundBottom && isLast ) ) @@ -64,20 +64,20 @@ public struct LuminareButtonCompose: View { LuminarePane { LuminareSection { Text("LuminareButtonCompose") - + LuminareButtonCompose(.bottom) { Button { print(1) } label: { Text("Button 1") } - + Button { print(1) } label: { Text("Button 1") } - + Button { print(2) } label: { From 2fcb560053a1d3d7fc8468bd114e781a702c9ca8 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:46:58 +0800 Subject: [PATCH 04/23] fix formatting --- .../Utilities/Extensions/EnvironmentValues+Extensions.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift b/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift index f65f2051..8d5c18e3 100644 --- a/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift @@ -92,9 +92,9 @@ public extension EnvironmentValues { @Entry var luminareSectionMaxWidth: CGFloat? = .infinity // MARK: ButtonCompose - + @Entry var luminareButtonComposeSpacing: CGFloat = 4 - + // MARK: Compose @Entry var luminareComposeControlSize: LuminareComposeControlSize = .automatic From 9edc1f72461612d1852451bd3d07750b0e3dfa7c Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:53:54 +0800 Subject: [PATCH 05/23] Rename SwiftUI Preview Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Luminare/Components/Composes/LuminareButtonCompose.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 4d8a01ee..b427ea64 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -58,7 +58,7 @@ public struct LuminareButtonCompose: View { @available(macOS 15.0, *) #Preview( - "LuminareButton", + "LuminareButtonCompose", traits: .sizeThatFitsLayout ) { LuminarePane { From 2232586e5c35d9094be055602cbe3ddd8b2d31d9 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:55:48 +0800 Subject: [PATCH 06/23] namespace LuminareButtonCompose.PositionInList --- .../Components/Composes/LuminareButtonCompose.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index b427ea64..6bb66e4c 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -14,10 +14,6 @@ public struct LuminareButtonBuilder { } } -public enum PositionInList { - case top, middle, bottom, unknown -} - public struct LuminareButtonCompose: View { @Environment(\.luminareButtonComposeSpacing) private var spacing @@ -54,6 +50,11 @@ public struct LuminareButtonCompose: View { } .buttonStyle(.luminare) } + + public enum PositionInList { + case top, middle, bottom, unknown + } + } @available(macOS 15.0, *) From e12073b4c79a2e798d7234161052070ff95e2fe7 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:57:26 +0800 Subject: [PATCH 07/23] fix formatting --- .../Luminare/Components/Composes/LuminareButtonCompose.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 6bb66e4c..2b400444 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -50,11 +50,9 @@ public struct LuminareButtonCompose: View { } .buttonStyle(.luminare) } - public enum PositionInList { case top, middle, bottom, unknown } - } @available(macOS 15.0, *) From a0e23f696acfa44ead5b3842a67a8e6d15fe6591 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 14:58:34 +0800 Subject: [PATCH 08/23] improve LuminareButtonBuilder --- .../Composes/LuminareButtonCompose.swift | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 2b400444..a6e7c744 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -9,9 +9,24 @@ import SwiftUI @resultBuilder public struct LuminareButtonBuilder { - public static func buildBlock(_ components: any View...) -> [any View] { + public static func buildExpression(_ expression: V) -> AnyView { + AnyView(expression) + } + public static func buildBlock(_ components: AnyView...) -> [AnyView] { components } + public static func buildOptional(_ component: [AnyView]?) -> [AnyView] { + component ?? [] + } + public static func buildEither(first component: [AnyView]) -> [AnyView] { + component + } + public static func buildEither(second component: [AnyView]) -> [AnyView] { + component + } + public static func buildArray(_ components: [[AnyView]]) -> [AnyView] { + components.flatMap { $0 } + } } public struct LuminareButtonCompose: View { From 96b5c25ca4b4d90b3f36b979c4c390d015f94d78 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:00:52 +0800 Subject: [PATCH 09/23] add .luminareButtonComposeSpacing modifier --- Sources/Luminare/Utilities/Extensions/View+Extensions.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Luminare/Utilities/Extensions/View+Extensions.swift b/Sources/Luminare/Utilities/Extensions/View+Extensions.swift index 9b1b6060..7820bfe3 100644 --- a/Sources/Luminare/Utilities/Extensions/View+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/View+Extensions.swift @@ -394,6 +394,10 @@ public extension View { ) } + func luminareButtonComposeSpacing(_ spacing: CGFloat) -> some View { + environment(\.luminareButtonComposeSpacing, spacing) + } + // MARK: Tool Tip func luminareToolTipTrigger(_ trigger: LuminareToolTipTrigger) -> some View { From f3afd291af0e595284927ac62b043ee2336d86db Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:02:27 +0800 Subject: [PATCH 10/23] improve ForEach --- .../Luminare/Components/Composes/LuminareButtonCompose.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index a6e7c744..7c4729a5 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -45,7 +45,9 @@ public struct LuminareButtonCompose: View { public var body: some View { HStack(spacing: spacing) { - ForEach(Array(buttons.enumerated()), id: \.offset) { index, button in + ForEach(buttons.indices, id: \.self) { index in + let button = buttons[index] + let isFirst = index == 0 let isLast = index == buttons.count - 1 From 42f9c9c7845f64a88da999caec8bcaca30491618 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:05:47 +0800 Subject: [PATCH 11/23] improve button paramter type --- .../Composes/LuminareButtonCompose.swift | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 7c4729a5..b783c8a7 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -32,12 +32,12 @@ public struct LuminareButtonBuilder { public struct LuminareButtonCompose: View { @Environment(\.luminareButtonComposeSpacing) private var spacing - private let buttons: [any View] + private let buttons: [AnyView] private let positionInList: PositionInList public init( _ positionInList: PositionInList = .unknown, - @LuminareButtonBuilder _ buttons: () -> [any View] + @LuminareButtonBuilder _ buttons: () -> [AnyView] ) { self.positionInList = positionInList self.buttons = buttons() @@ -54,15 +54,14 @@ public struct LuminareButtonCompose: View { let roundTop = positionInList == .unknown || positionInList == .top let roundBottom = positionInList == .unknown || positionInList == .bottom - AnyView( - button - .luminareRoundingBehavior( - topLeading: roundTop && isFirst, - topTrailing: roundTop && isLast, - bottomLeading: roundBottom && isFirst, - bottomTrailing: roundBottom && isLast - ) - ) + + button + .luminareRoundingBehavior( + topLeading: roundTop && isFirst, + topTrailing: roundTop && isLast, + bottomLeading: roundBottom && isFirst, + bottomTrailing: roundBottom && isLast + ) } } .buttonStyle(.luminare) From 169211d4c4dba557735268b276c3e014ac29fae0 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:09:26 +0800 Subject: [PATCH 12/23] fix formatting + syntax --- .../Components/Composes/LuminareButtonCompose.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index b783c8a7..8611828b 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -9,23 +9,28 @@ import SwiftUI @resultBuilder public struct LuminareButtonBuilder { - public static func buildExpression(_ expression: V) -> AnyView { + public static func buildExpression(_ expression: some View) -> AnyView { AnyView(expression) } + public static func buildBlock(_ components: AnyView...) -> [AnyView] { components } + public static func buildOptional(_ component: [AnyView]?) -> [AnyView] { component ?? [] } + public static func buildEither(first component: [AnyView]) -> [AnyView] { component } + public static func buildEither(second component: [AnyView]) -> [AnyView] { component } + public static func buildArray(_ components: [[AnyView]]) -> [AnyView] { - components.flatMap { $0 } + components.flatMap(\.self) } } @@ -54,7 +59,6 @@ public struct LuminareButtonCompose: View { let roundTop = positionInList == .unknown || positionInList == .top let roundBottom = positionInList == .unknown || positionInList == .bottom - button .luminareRoundingBehavior( topLeading: roundTop && isFirst, @@ -66,6 +70,7 @@ public struct LuminareButtonCompose: View { } .buttonStyle(.luminare) } + public enum PositionInList { case top, middle, bottom, unknown } From fdda7ebc88861e1ac94602ef15edb08559ed9def Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:18:20 +0800 Subject: [PATCH 13/23] fix the builder component type inconsistency + add comments --- .../Components/Composes/LuminareButtonCompose.swift | 8 ++++---- .../Luminare/Utilities/Extensions/View+Extensions.swift | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 8611828b..9073695c 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -9,12 +9,12 @@ import SwiftUI @resultBuilder public struct LuminareButtonBuilder { - public static func buildExpression(_ expression: some View) -> AnyView { - AnyView(expression) + public static func buildExpression(_ expression: some View) -> [AnyView] { + [AnyView(expression)] } - public static func buildBlock(_ components: AnyView...) -> [AnyView] { - components + public static func buildBlock(_ components: [AnyView]...) -> [AnyView] { + components.flatMap(\.self) } public static func buildOptional(_ component: [AnyView]?) -> [AnyView] { diff --git a/Sources/Luminare/Utilities/Extensions/View+Extensions.swift b/Sources/Luminare/Utilities/Extensions/View+Extensions.swift index 7820bfe3..2d722511 100644 --- a/Sources/Luminare/Utilities/Extensions/View+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/View+Extensions.swift @@ -394,6 +394,8 @@ public extension View { ) } + // MARK: ButtonCompose + func luminareButtonComposeSpacing(_ spacing: CGFloat) -> some View { environment(\.luminareButtonComposeSpacing, spacing) } From 19b615be06a2659e4f4afaf7961efec87aea401a Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:27:10 +0800 Subject: [PATCH 14/23] use VariadicViews --- .../Composes/LuminareButtonCompose.swift | 83 +++++++++---------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 9073695c..45f8698b 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -6,60 +6,58 @@ // import SwiftUI +import VariadicViews -@resultBuilder -public struct LuminareButtonBuilder { - public static func buildExpression(_ expression: some View) -> [AnyView] { - [AnyView(expression)] - } - - public static func buildBlock(_ components: [AnyView]...) -> [AnyView] { - components.flatMap(\.self) - } - - public static func buildOptional(_ component: [AnyView]?) -> [AnyView] { - component ?? [] - } - - public static func buildEither(first component: [AnyView]) -> [AnyView] { - component - } - - public static func buildEither(second component: [AnyView]) -> [AnyView] { - component - } - - public static func buildArray(_ components: [[AnyView]]) -> [AnyView] { - components.flatMap(\.self) - } +public enum LuminareButtonComposePosition { + case top, middle, bottom, unknown } -public struct LuminareButtonCompose: View { +public struct LuminareButtonCompose: View { @Environment(\.luminareButtonComposeSpacing) private var spacing - private let buttons: [AnyView] - private let positionInList: PositionInList + private let positionInList: LuminareButtonComposePosition + private let content: () -> Content public init( - _ positionInList: PositionInList = .unknown, - @LuminareButtonBuilder _ buttons: () -> [AnyView] + _ positionInList: LuminareButtonComposePosition = .unknown, + @ViewBuilder _ content: @escaping () -> Content ) { self.positionInList = positionInList - self.buttons = buttons() + self.content = content } public var body: some View { - HStack(spacing: spacing) { - ForEach(buttons.indices, id: \.self) { index in - let button = buttons[index] + UnaryVariadicView(content()) { children in + LuminareButtonComposeLayout( + children: children, + spacing: spacing, + positionInList: positionInList + ) + } + .buttonStyle(.luminare) + } +} + +// MARK: - Layout + +struct LuminareButtonComposeLayout: View { + let children: VariadicViewChildren + let spacing: CGFloat + let positionInList: LuminareButtonComposePosition - let isFirst = index == 0 - let isLast = index == buttons.count - 1 + var body: some View { + let first = children.first?.id + let last = children.last?.id - let roundTop = positionInList == .unknown || positionInList == .top - let roundBottom = positionInList == .unknown || positionInList == .bottom + let roundTop = positionInList == .unknown || positionInList == .top + let roundBottom = positionInList == .unknown || positionInList == .bottom - button + HStack(spacing: spacing) { + ForEach(children) { child in + let isFirst = child.id == first + let isLast = child.id == last + + child .luminareRoundingBehavior( topLeading: roundTop && isFirst, topTrailing: roundTop && isLast, @@ -68,14 +66,11 @@ public struct LuminareButtonCompose: View { ) } } - .buttonStyle(.luminare) - } - - public enum PositionInList { - case top, middle, bottom, unknown } } +// MARK: - Preview + @available(macOS 15.0, *) #Preview( "LuminareButtonCompose", From 41f19b6a716fbd09c14772bf45947f768385efe7 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 15:28:03 +0800 Subject: [PATCH 15/23] fix comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Utilities/Extensions/EnvironmentValues+Extensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift b/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift index 8d5c18e3..4f86073c 100644 --- a/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift @@ -91,7 +91,7 @@ public extension EnvironmentValues { /// If 0, then luminareSection will be of fixed size. @Entry var luminareSectionMaxWidth: CGFloat? = .infinity - // MARK: ButtonCompose + // MARK: Button Compose @Entry var luminareButtonComposeSpacing: CGFloat = 4 From 6bf80a27798ee10a6f53ee5a99ad20e4d83ecf7e Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 16:52:16 +0800 Subject: [PATCH 16/23] Add Equatable conformance to LuminareButtonComposePosition Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Luminare/Components/Composes/LuminareButtonCompose.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 45f8698b..463f9f17 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -8,7 +8,7 @@ import SwiftUI import VariadicViews -public enum LuminareButtonComposePosition { +public enum LuminareButtonComposePosition: Equatable { case top, middle, bottom, unknown } From b2288fe213f41d4fba101aa33d605012ff165d38 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 17:17:36 +0800 Subject: [PATCH 17/23] fix comment --- Sources/Luminare/Utilities/Extensions/View+Extensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Luminare/Utilities/Extensions/View+Extensions.swift b/Sources/Luminare/Utilities/Extensions/View+Extensions.swift index 2d722511..c4bbd91b 100644 --- a/Sources/Luminare/Utilities/Extensions/View+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/View+Extensions.swift @@ -394,7 +394,7 @@ public extension View { ) } - // MARK: ButtonCompose + // MARK: Button Compose func luminareButtonComposeSpacing(_ spacing: CGFloat) -> some View { environment(\.luminareButtonComposeSpacing, spacing) From 2b46a084930750d6f8cc4aa4daf5261d0a60eef2 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 17:45:53 +0800 Subject: [PATCH 18/23] fix LuminareButtonCompose --- .../Composes/LuminareButtonCompose.swift | 91 +++++++++++-------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 463f9f17..8d6fa03e 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -6,58 +6,67 @@ // import SwiftUI -import VariadicViews -public enum LuminareButtonComposePosition: Equatable { +public enum LuminareButtonComposePosition { case top, middle, bottom, unknown } -public struct LuminareButtonCompose: View { +@resultBuilder +public struct LuminareButtonBuilder { + public static func buildExpression(_ expression: some View) -> [AnyView] { + [AnyView(expression)] + } + + public static func buildBlock(_ components: [AnyView]...) -> [AnyView] { + components.flatMap(\.self) + } + + public static func buildOptional(_ component: [AnyView]?) -> [AnyView] { + component ?? [] + } + + public static func buildEither(first component: [AnyView]) -> [AnyView] { + component + } + + public static func buildEither(second component: [AnyView]) -> [AnyView] { + component + } + + public static func buildArray(_ components: [[AnyView]]) -> [AnyView] { + components.flatMap(\.self) + } + + public static func buildLimitedAvailability(_ component: [AnyView]) -> [AnyView] { + component + } +} + +public struct LuminareButtonCompose: View { @Environment(\.luminareButtonComposeSpacing) private var spacing + private let buttons: [AnyView] private let positionInList: LuminareButtonComposePosition - private let content: () -> Content public init( _ positionInList: LuminareButtonComposePosition = .unknown, - @ViewBuilder _ content: @escaping () -> Content + @LuminareButtonBuilder _ buttons: () -> [AnyView] ) { self.positionInList = positionInList - self.content = content + self.buttons = buttons() } public var body: some View { - UnaryVariadicView(content()) { children in - LuminareButtonComposeLayout( - children: children, - spacing: spacing, - positionInList: positionInList - ) - } - .buttonStyle(.luminare) - } -} - -// MARK: - Layout - -struct LuminareButtonComposeLayout: View { - let children: VariadicViewChildren - let spacing: CGFloat - let positionInList: LuminareButtonComposePosition - - var body: some View { - let first = children.first?.id - let last = children.last?.id - - let roundTop = positionInList == .unknown || positionInList == .top - let roundBottom = positionInList == .unknown || positionInList == .bottom - HStack(spacing: spacing) { - ForEach(children) { child in - let isFirst = child.id == first - let isLast = child.id == last + ForEach(buttons.indices, id: \.self) { index in + let button = buttons[index] + let isFirst = index == 0 + let isLast = index == buttons.count - 1 - child + let roundTop = positionInList == .unknown || positionInList == .top + let roundBottom = positionInList == .unknown || positionInList == .bottom + + button .luminareRoundingBehavior( topLeading: roundTop && isFirst, topTrailing: roundTop && isLast, @@ -66,6 +75,7 @@ struct LuminareButtonComposeLayout: View { ) } } + .buttonStyle(.luminare) } } @@ -78,7 +88,8 @@ struct LuminareButtonComposeLayout: View { ) { LuminarePane { LuminareSection { - Text("LuminareButtonCompose") + Text("Other content ...") + .foregroundStyle(.secondary) LuminareButtonCompose(.bottom) { Button { @@ -88,15 +99,15 @@ struct LuminareButtonComposeLayout: View { } Button { - print(1) + print(2) } label: { - Text("Button 1") + Text("Button 2") } Button { - print(2) + print(3) } label: { - Text("Button 2") + Text("Button 3") } } } From 890c80cd643744a223802151596939a677e3439f Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Mon, 23 Mar 2026 17:58:23 +0800 Subject: [PATCH 19/23] Add documentation for LuminareButtonCompose --- .../Compose/LuminareButtonCompose.md | 43 ++++++++++++++++++ .../LuminareButtonCompose@1x.png | Bin 0 -> 17308 bytes .../LuminareButtonCompose~dark@1x.png | Bin 0 -> 28219 bytes 3 files changed, 43 insertions(+) create mode 100644 Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md create mode 100644 Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose@1x.png create mode 100644 Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose~dark@1x.png diff --git a/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md new file mode 100644 index 00000000..356ffdab --- /dev/null +++ b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md @@ -0,0 +1,43 @@ +# ``Luminare/LuminareButtonCompose`` + +A horizontalrow of Luminare buttons. + +With optional parameter ``LuminareButtonComposePosition``, to set whether this is at the bottom or top of a list to adjust button rounding behaviour. + +@Row { + @Column { + ```swift + LuminareButtonCompose(.bottom) { + Button { + print(1) + } label: { + Text("Button 1") + } + + Button { + print(2) + } label: { + Text("Button 2") + } + + Button { + print(3) + } label: { + Text("Button 3") + } + } + ``` + } +} + +@Row { + @Column { + ![LuminareButtonCompose](LuminareButtonCompose) + } +} + +## Topics + +### Position + +- ``LuminareButtonComposePosition`` diff --git a/Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose@1x.png b/Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..42d588bbc74bc5eca0bbf331e60f23253551b223 GIT binary patch literal 17308 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 Date: Thu, 26 Mar 2026 18:29:45 +0800 Subject: [PATCH 20/23] use luminare rounding enironment value instead of explicit --- .../Composes/LuminareButtonCompose.swift | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift index 8d6fa03e..e688ab11 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift @@ -7,10 +7,6 @@ import SwiftUI -public enum LuminareButtonComposePosition { - case top, middle, bottom, unknown -} - @resultBuilder public struct LuminareButtonBuilder { public static func buildExpression(_ expression: some View) -> [AnyView] { @@ -45,14 +41,16 @@ public struct LuminareButtonBuilder { public struct LuminareButtonCompose: View { @Environment(\.luminareButtonComposeSpacing) private var spacing + @Environment(\.luminareTopLeadingRounded) private var topLeadingRounded + @Environment(\.luminareTopTrailingRounded) private var topTrailingRounded + @Environment(\.luminareBottomLeadingRounded) private var bottomLeadingRounded + @Environment(\.luminareBottomTrailingRounded) private var bottomTrailingRounded + private let buttons: [AnyView] - private let positionInList: LuminareButtonComposePosition public init( - _ positionInList: LuminareButtonComposePosition = .unknown, @LuminareButtonBuilder _ buttons: () -> [AnyView] ) { - self.positionInList = positionInList self.buttons = buttons() } @@ -63,15 +61,12 @@ public struct LuminareButtonCompose: View { let isFirst = index == 0 let isLast = index == buttons.count - 1 - let roundTop = positionInList == .unknown || positionInList == .top - let roundBottom = positionInList == .unknown || positionInList == .bottom - button .luminareRoundingBehavior( - topLeading: roundTop && isFirst, - topTrailing: roundTop && isLast, - bottomLeading: roundBottom && isFirst, - bottomTrailing: roundBottom && isLast + topLeading: isFirst ? topLeadingRounded : false, + topTrailing: isLast ? topTrailingRounded : false, + bottomLeading: isFirst ? bottomLeadingRounded : false, + bottomTrailing: isLast ? bottomTrailingRounded : false ) } } @@ -88,10 +83,7 @@ public struct LuminareButtonCompose: View { ) { LuminarePane { LuminareSection { - Text("Other content ...") - .foregroundStyle(.secondary) - - LuminareButtonCompose(.bottom) { + LuminareButtonCompose { Button { print(1) } label: { @@ -110,6 +102,10 @@ public struct LuminareButtonCompose: View { Text("Button 3") } } + .luminareRoundingBehavior(top: true) + + Text("Other content ...") + .foregroundStyle(.secondary) } } } From d79b8985a94ecf055896fd0af3e6e33a09a07c93 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Thu, 26 Mar 2026 19:18:34 +0800 Subject: [PATCH 21/23] lint --- ...{LuminareButtonCompose.swift => LuminareButtonRow.swift} | 6 +++--- Sources/Luminare/Components/Stepper/LuminareStepper.swift | 3 +-- .../Components/Compose/LuminareButtonCompose.md | 6 +++--- .../Luminare/Utilities/Extensions/Color+Extensions.swift | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) rename Sources/Luminare/Components/Composes/{LuminareButtonCompose.swift => LuminareButtonRow.swift} (96%) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift b/Sources/Luminare/Components/Composes/LuminareButtonRow.swift similarity index 96% rename from Sources/Luminare/Components/Composes/LuminareButtonCompose.swift rename to Sources/Luminare/Components/Composes/LuminareButtonRow.swift index e688ab11..c01d333b 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonCompose.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonRow.swift @@ -38,7 +38,7 @@ public struct LuminareButtonBuilder { } } -public struct LuminareButtonCompose: View { +public struct LuminareButtonRow: View { @Environment(\.luminareButtonComposeSpacing) private var spacing @Environment(\.luminareTopLeadingRounded) private var topLeadingRounded @@ -78,12 +78,12 @@ public struct LuminareButtonCompose: View { @available(macOS 15.0, *) #Preview( - "LuminareButtonCompose", + "LuminareButtonRow", traits: .sizeThatFitsLayout ) { LuminarePane { LuminareSection { - LuminareButtonCompose { + LuminareButtonRow { Button { print(1) } label: { diff --git a/Sources/Luminare/Components/Stepper/LuminareStepper.swift b/Sources/Luminare/Components/Stepper/LuminareStepper.swift index 5d55d388..59b8f9a6 100644 --- a/Sources/Luminare/Components/Stepper/LuminareStepper.swift +++ b/Sources/Luminare/Components/Stepper/LuminareStepper.swift @@ -440,8 +440,7 @@ public struct LuminareStepper: View where V: Strideable & BinaryFloatingPoint private func magnifyFactor(at index: Int) -> CGFloat { let standardDeviation = 0.5 - let value = bellCurve(shift(at: index), standardDeviation: standardDeviation) - return value + return bellCurve(shift(at: index), standardDeviation: standardDeviation) } private func blurFactor(at index: Int) -> CGFloat { diff --git a/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md index 356ffdab..223a19f0 100644 --- a/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md +++ b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md @@ -1,13 +1,13 @@ -# ``Luminare/LuminareButtonCompose`` +# ``Luminare/LuminareButtonRow`` A horizontalrow of Luminare buttons. -With optional parameter ``LuminareButtonComposePosition``, to set whether this is at the bottom or top of a list to adjust button rounding behaviour. + @Row { @Column { ```swift - LuminareButtonCompose(.bottom) { + LuminareButtonRow { Button { print(1) } label: { diff --git a/Sources/Luminare/Utilities/Extensions/Color+Extensions.swift b/Sources/Luminare/Utilities/Extensions/Color+Extensions.swift index 9f7abc7a..5261a974 100644 --- a/Sources/Luminare/Utilities/Extensions/Color+Extensions.swift +++ b/Sources/Luminare/Utilities/Extensions/Color+Extensions.swift @@ -9,7 +9,7 @@ import AppKit import SwiftUI /// A shorthand for storing colors in hue-saturation-brightness format -struct HSBColor: Equatable, Hashable, Codable, Sendable { +struct HSBColor: Equatable, Hashable, Codable { var hue: Double var saturation: Double var brightness: Double From f3698c49d70339537dcd51fc78912edc7e2bdb43 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Thu, 26 Mar 2026 19:20:37 +0800 Subject: [PATCH 22/23] lint --- Sources/Luminare/Components/Composes/LuminareButtonRow.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Luminare/Components/Composes/LuminareButtonRow.swift b/Sources/Luminare/Components/Composes/LuminareButtonRow.swift index c01d333b..4b113dee 100644 --- a/Sources/Luminare/Components/Composes/LuminareButtonRow.swift +++ b/Sources/Luminare/Components/Composes/LuminareButtonRow.swift @@ -1,5 +1,5 @@ // -// LuminareButtonCompose.swift +// LuminareButtonRow.swift // Luminare // // Created by Adon Omeri on 23/3/2026. @@ -83,7 +83,7 @@ public struct LuminareButtonRow: View { ) { LuminarePane { LuminareSection { - LuminareButtonRow { + LuminareButtonRow { Button { print(1) } label: { From 350eff82e756b25214604f5063935422013af163 Mon Sep 17 00:00:00 2001 From: Adon Omeri Date: Thu, 26 Mar 2026 19:30:40 +0800 Subject: [PATCH 23/23] update documentation --- .../Compose/LuminareButtonCompose.md | 43 ------------------ .../Components/Compose/LuminareButtonRow.md | 37 +++++++++++++++ .../LuminareButtonRow@1x.png} | Bin .../LuminareButtonRow~dark@1x.png} | Bin 4 files changed, 37 insertions(+), 43 deletions(-) delete mode 100644 Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md create mode 100644 Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonRow.md rename Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/{LuminareButtonCompose/LuminareButtonCompose@1x.png => LuminareButtonRow/LuminareButtonRow@1x.png} (100%) rename Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/{LuminareButtonCompose/LuminareButtonCompose~dark@1x.png => LuminareButtonRow/LuminareButtonRow~dark@1x.png} (100%) diff --git a/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md deleted file mode 100644 index 223a19f0..00000000 --- a/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonCompose.md +++ /dev/null @@ -1,43 +0,0 @@ -# ``Luminare/LuminareButtonRow`` - -A horizontalrow of Luminare buttons. - - - -@Row { - @Column { - ```swift - LuminareButtonRow { - Button { - print(1) - } label: { - Text("Button 1") - } - - Button { - print(2) - } label: { - Text("Button 2") - } - - Button { - print(3) - } label: { - Text("Button 3") - } - } - ``` - } -} - -@Row { - @Column { - ![LuminareButtonCompose](LuminareButtonCompose) - } -} - -## Topics - -### Position - -- ``LuminareButtonComposePosition`` diff --git a/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonRow.md b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonRow.md new file mode 100644 index 00000000..21e45421 --- /dev/null +++ b/Sources/Luminare/Luminare.docc/Components/Compose/LuminareButtonRow.md @@ -0,0 +1,37 @@ +# ``Luminare/LuminareButtonRow`` + +A horizontalrow of Luminare buttons. + + + +@Row { + @Column { + ```swift + LuminareButtonRow { + Button { + print(1) + } label: { + Text("Button 1") + } + + Button { + print(2) + } label: { + Text("Button 2") + } + + Button { + print(3) + } label: { + Text("Button 3") + } + } + ``` + } +} + +@Row { + @Column { + ![LuminareButtonRow](LuminareButtonRow) + } +} diff --git a/Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose@1x.png b/Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonRow/LuminareButtonRow@1x.png similarity index 100% rename from Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose@1x.png rename to Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonRow/LuminareButtonRow@1x.png diff --git a/Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose~dark@1x.png b/Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonRow/LuminareButtonRow~dark@1x.png similarity index 100% rename from Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonCompose/LuminareButtonCompose~dark@1x.png rename to Sources/Luminare/Luminare.docc/Resources/Previews Screenshots/Compose/LuminareButtonRow/LuminareButtonRow~dark@1x.png