mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
Merge pull request #11 from phranck/feature/parameter-packs-container-cleanup
refactor: Parameter Packs, Container cleanup, precompiled ANSI Regex (A.8, A.10, H.7)
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ let package = Package(
|
||||
// Minimum deployment targets for Apple platforms
|
||||
// Linux is automatically supported (no platform specification needed)
|
||||
platforms: [
|
||||
.macOS(.v13)
|
||||
.macOS(.v14)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
||||
@@ -1,143 +1,30 @@
|
||||
// swiftlint:disable large_tuple
|
||||
//
|
||||
// TupleViews.swift
|
||||
// TUIKit
|
||||
//
|
||||
// Container types for multiple views in ViewBuilder.
|
||||
// Container type for multiple views in ViewBuilder, using Swift Parameter Packs.
|
||||
//
|
||||
|
||||
// MARK: - TupleView2
|
||||
/// A view that contains multiple child views packed via a parameter pack.
|
||||
///
|
||||
/// `TupleView` replaces the previous `TupleView2` through `TupleView10`
|
||||
/// types with a single generic struct using Swift Parameter Packs (SE-0393).
|
||||
/// This removes the 10-child limit and eliminates ~400 lines of boilerplate.
|
||||
///
|
||||
/// `TupleView` is created automatically by `ViewBuilder` when multiple
|
||||
/// views appear in a `@ViewBuilder` closure.
|
||||
public struct TupleView<each V: View>: View {
|
||||
/// The packed child views.
|
||||
public let children: (repeat each V)
|
||||
|
||||
/// A view that contains two child views.
|
||||
public struct TupleView2<V0: View, V1: View>: View {
|
||||
public let value: (V0, V1)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1) {
|
||||
self.value = (v0, v1)
|
||||
/// Creates a tuple view from a parameter pack of child views.
|
||||
///
|
||||
/// - Parameter children: The child views.
|
||||
public init(_ children: repeat each V) {
|
||||
self.children = (repeat each children)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView2 renders its children directly")
|
||||
fatalError("TupleView renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView3
|
||||
|
||||
/// A view that contains three child views.
|
||||
public struct TupleView3<V0: View, V1: View, V2: View>: View {
|
||||
public let value: (V0, V1, V2)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2) {
|
||||
self.value = (v0, v1, v2)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView3 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView4
|
||||
|
||||
/// A view that contains four child views.
|
||||
public struct TupleView4<V0: View, V1: View, V2: View, V3: View>: View {
|
||||
public let value: (V0, V1, V2, V3)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3) {
|
||||
self.value = (v0, v1, v2, v3)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView4 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView5
|
||||
|
||||
/// A view that contains five child views.
|
||||
public struct TupleView5<V0: View, V1: View, V2: View, V3: View, V4: View>: View {
|
||||
public let value: (V0, V1, V2, V3, V4)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4) {
|
||||
self.value = (v0, v1, v2, v3, v4)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView5 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView6
|
||||
|
||||
/// A view that contains six child views.
|
||||
public struct TupleView6<V0: View, V1: View, V2: View, V3: View, V4: View, V5: View>: View {
|
||||
public let value: (V0, V1, V2, V3, V4, V5)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4, _ v5: V5) {
|
||||
self.value = (v0, v1, v2, v3, v4, v5)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView6 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView7
|
||||
|
||||
/// A view that contains seven child views.
|
||||
public struct TupleView7<V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View>: View {
|
||||
public let value: (V0, V1, V2, V3, V4, V5, V6)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4, _ v5: V5, _ v6: V6) {
|
||||
self.value = (v0, v1, v2, v3, v4, v5, v6)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView7 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView8
|
||||
|
||||
/// A view that contains eight child views.
|
||||
public struct TupleView8<V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View>: View {
|
||||
public let value: (V0, V1, V2, V3, V4, V5, V6, V7)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4, _ v5: V5, _ v6: V6, _ v7: V7) {
|
||||
self.value = (v0, v1, v2, v3, v4, v5, v6, v7)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView8 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView9
|
||||
|
||||
/// A view that contains nine child views.
|
||||
public struct TupleView9<V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View>: View {
|
||||
public let value: (V0, V1, V2, V3, V4, V5, V6, V7, V8)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4, _ v5: V5, _ v6: V6, _ v7: V7, _ v8: V8) {
|
||||
self.value = (v0, v1, v2, v3, v4, v5, v6, v7, v8)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView9 renders its children directly")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TupleView10
|
||||
|
||||
/// A view that contains ten child views.
|
||||
public struct TupleView10<V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, V9: View>: View {
|
||||
public let value: (V0, V1, V2, V3, V4, V5, V6, V7, V8, V9)
|
||||
|
||||
public init(_ v0: V0, _ v1: V1, _ v2: V2, _ v3: V3, _ v4: V4, _ v5: V5, _ v6: V6, _ v7: V7, _ v8: V8, _ v9: V9) {
|
||||
self.value = (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9)
|
||||
}
|
||||
|
||||
public var body: Never {
|
||||
fatalError("TupleView10 renders its children directly")
|
||||
}
|
||||
}
|
||||
// swiftlint:enable large_tuple
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// swiftlint:disable function_parameter_count
|
||||
//
|
||||
// ViewBuilder.swift
|
||||
// TUIKit
|
||||
@@ -22,7 +21,7 @@
|
||||
///
|
||||
/// The builder supports:
|
||||
/// - Single views
|
||||
/// - Multiple views (up to 10)
|
||||
/// - Multiple views (unlimited, via Parameter Packs)
|
||||
/// - Conditionals (`if`, `if-else`)
|
||||
/// - Optional views (`if let`)
|
||||
/// - Arrays of views (`for-in`)
|
||||
@@ -36,114 +35,16 @@ public struct ViewBuilder {
|
||||
content
|
||||
}
|
||||
|
||||
// MARK: - Multiple Views (Tuple Views)
|
||||
// MARK: - Multiple Views (Parameter Pack)
|
||||
|
||||
/// Builds two views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1
|
||||
) -> TupleView2<C0, C1> {
|
||||
TupleView2(c0, c1)
|
||||
}
|
||||
|
||||
/// Builds three views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2
|
||||
) -> TupleView3<C0, C1, C2> {
|
||||
TupleView3(c0, c1, c2)
|
||||
}
|
||||
|
||||
/// Builds four views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3
|
||||
) -> TupleView4<C0, C1, C2, C3> {
|
||||
TupleView4(c0, c1, c2, c3)
|
||||
}
|
||||
|
||||
/// Builds five views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View, C4: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3,
|
||||
_ c4: C4
|
||||
) -> TupleView5<C0, C1, C2, C3, C4> {
|
||||
TupleView5(c0, c1, c2, c3, c4)
|
||||
}
|
||||
|
||||
/// Builds six views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View, C4: View, C5: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3,
|
||||
_ c4: C4,
|
||||
_ c5: C5
|
||||
) -> TupleView6<C0, C1, C2, C3, C4, C5> {
|
||||
TupleView6(c0, c1, c2, c3, c4, c5)
|
||||
}
|
||||
|
||||
/// Builds seven views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3,
|
||||
_ c4: C4,
|
||||
_ c5: C5,
|
||||
_ c6: C6
|
||||
) -> TupleView7<C0, C1, C2, C3, C4, C5, C6> {
|
||||
TupleView7(c0, c1, c2, c3, c4, c5, c6)
|
||||
}
|
||||
|
||||
/// Builds eight views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3,
|
||||
_ c4: C4,
|
||||
_ c5: C5,
|
||||
_ c6: C6,
|
||||
_ c7: C7
|
||||
) -> TupleView8<C0, C1, C2, C3, C4, C5, C6, C7> {
|
||||
TupleView8(c0, c1, c2, c3, c4, c5, c6, c7)
|
||||
}
|
||||
|
||||
/// Builds nine views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3,
|
||||
_ c4: C4,
|
||||
_ c5: C5,
|
||||
_ c6: C6,
|
||||
_ c7: C7,
|
||||
_ c8: C8
|
||||
) -> TupleView9<C0, C1, C2, C3, C4, C5, C6, C7, C8> {
|
||||
TupleView9(c0, c1, c2, c3, c4, c5, c6, c7, c8)
|
||||
}
|
||||
|
||||
/// Builds ten views into a TupleView.
|
||||
public static func buildBlock<C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View>(
|
||||
_ c0: C0,
|
||||
_ c1: C1,
|
||||
_ c2: C2,
|
||||
_ c3: C3,
|
||||
_ c4: C4,
|
||||
_ c5: C5,
|
||||
_ c6: C6,
|
||||
_ c7: C7,
|
||||
_ c8: C8,
|
||||
_ c9: C9
|
||||
) -> TupleView10<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9> {
|
||||
TupleView10(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)
|
||||
/// Builds multiple views into a TupleView using Swift Parameter Packs.
|
||||
///
|
||||
/// This single overload replaces the previous 9 arity-specific `buildBlock`
|
||||
/// overloads (`TupleView2` through `TupleView10`), removing the 10-child limit.
|
||||
public static func buildBlock<each C: View>(
|
||||
_ content: repeat each C
|
||||
) -> TupleView<repeat each C> {
|
||||
TupleView(repeat each content)
|
||||
}
|
||||
|
||||
// MARK: - Conditionals
|
||||
@@ -191,4 +92,3 @@ public struct ViewBuilder {
|
||||
expression
|
||||
}
|
||||
}
|
||||
// swiftlint:enable function_parameter_count
|
||||
|
||||
@@ -15,11 +15,7 @@ extension String {
|
||||
|
||||
/// The string with all ANSI escape codes removed.
|
||||
var stripped: String {
|
||||
replacingOccurrences(
|
||||
of: ANSIRenderer.ansiPattern,
|
||||
with: "",
|
||||
options: .regularExpression
|
||||
)
|
||||
replacing(ANSIRenderer.ansiRegex, with: "")
|
||||
}
|
||||
|
||||
/// Pads the string to the specified visible width using spaces.
|
||||
|
||||
@@ -22,8 +22,12 @@ public enum ANSIRenderer {
|
||||
/// Dim/faint text style code.
|
||||
public static let dim = "\(csi)2m"
|
||||
|
||||
/// Regex pattern that matches any ANSI escape sequence.
|
||||
public static let ansiPattern = "\u{1B}\\[[0-9;]*[a-zA-Z]"
|
||||
/// Precompiled regex that matches any ANSI escape sequence.
|
||||
///
|
||||
/// Used by `String.stripped` and `String.strippedLength` to remove
|
||||
/// formatting codes for visible-width calculations. Compiling once
|
||||
/// avoids per-call overhead in the hot rendering path.
|
||||
public static nonisolated(unsafe) let ansiRegex = /\u{1B}\[[0-9;]*[a-zA-Z]/
|
||||
|
||||
// MARK: - SGR Style Codes
|
||||
|
||||
|
||||
@@ -227,156 +227,15 @@ extension ZStack: Renderable {
|
||||
|
||||
// MARK: - TupleView Rendering + ChildInfoProvider
|
||||
|
||||
extension TupleView2: Renderable, ChildInfoProvider {
|
||||
extension TupleView: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView3: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView4: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView5: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
makeChildInfo(for: value.4, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView6: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
makeChildInfo(for: value.4, context: context),
|
||||
makeChildInfo(for: value.5, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView7: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
makeChildInfo(for: value.4, context: context),
|
||||
makeChildInfo(for: value.5, context: context),
|
||||
makeChildInfo(for: value.6, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView8: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
makeChildInfo(for: value.4, context: context),
|
||||
makeChildInfo(for: value.5, context: context),
|
||||
makeChildInfo(for: value.6, context: context),
|
||||
makeChildInfo(for: value.7, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView9: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
makeChildInfo(for: value.4, context: context),
|
||||
makeChildInfo(for: value.5, context: context),
|
||||
makeChildInfo(for: value.6, context: context),
|
||||
makeChildInfo(for: value.7, context: context),
|
||||
makeChildInfo(for: value.8, context: context),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension TupleView10: Renderable, ChildInfoProvider {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
FrameBuffer(verticallyStacking: childInfos(context: context).compactMap(\.buffer))
|
||||
}
|
||||
|
||||
func childInfos(context: RenderContext) -> [ChildInfo] {
|
||||
[
|
||||
makeChildInfo(for: value.0, context: context),
|
||||
makeChildInfo(for: value.1, context: context),
|
||||
makeChildInfo(for: value.2, context: context),
|
||||
makeChildInfo(for: value.3, context: context),
|
||||
makeChildInfo(for: value.4, context: context),
|
||||
makeChildInfo(for: value.5, context: context),
|
||||
makeChildInfo(for: value.6, context: context),
|
||||
makeChildInfo(for: value.7, context: context),
|
||||
makeChildInfo(for: value.8, context: context),
|
||||
makeChildInfo(for: value.9, context: context),
|
||||
]
|
||||
var infos: [ChildInfo] = []
|
||||
repeat infos.append(makeChildInfo(for: each children, context: context))
|
||||
return infos
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,20 +89,9 @@ public struct Alert<Actions: View>: View {
|
||||
extension Alert: Renderable {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
let hasActions = !(actions is EmptyView)
|
||||
let effectiveConfig =
|
||||
hasActions
|
||||
? config
|
||||
: ContainerConfig(
|
||||
borderStyle: config.borderStyle,
|
||||
borderColor: config.borderColor,
|
||||
titleColor: config.titleColor,
|
||||
padding: config.padding,
|
||||
showFooterSeparator: false
|
||||
)
|
||||
|
||||
return renderContainer(
|
||||
title: title,
|
||||
config: effectiveConfig,
|
||||
config: config,
|
||||
content: Text(message),
|
||||
footer: hasActions ? actions : nil,
|
||||
context: context
|
||||
|
||||
@@ -139,36 +139,27 @@ internal func renderContainer<Content: View, Footer: View>(
|
||||
footer: Footer?,
|
||||
context: RenderContext
|
||||
) -> FrameBuffer {
|
||||
let containerStyle = ContainerStyle(from: config)
|
||||
let hasFooter = footer != nil
|
||||
let style = ContainerStyle(
|
||||
showHeaderSeparator: true,
|
||||
showFooterSeparator: hasFooter && config.showFooterSeparator,
|
||||
borderStyle: config.borderStyle,
|
||||
borderColor: config.borderColor
|
||||
)
|
||||
|
||||
if let footerView = footer {
|
||||
let container = ContainerView(
|
||||
title: title,
|
||||
titleColor: config.titleColor,
|
||||
style: containerStyle,
|
||||
padding: config.padding
|
||||
) {
|
||||
content
|
||||
} footer: {
|
||||
let container = ContainerView(
|
||||
title: title,
|
||||
titleColor: config.titleColor,
|
||||
style: style,
|
||||
padding: config.padding
|
||||
) {
|
||||
content
|
||||
} footer: {
|
||||
if let footerView = footer {
|
||||
footerView
|
||||
}
|
||||
return container.renderToBuffer(context: context)
|
||||
} else {
|
||||
let container = ContainerView(
|
||||
title: title,
|
||||
titleColor: config.titleColor,
|
||||
style: ContainerStyle(
|
||||
showHeaderSeparator: true,
|
||||
showFooterSeparator: false,
|
||||
borderStyle: config.borderStyle,
|
||||
borderColor: config.borderColor
|
||||
),
|
||||
padding: config.padding
|
||||
) {
|
||||
content
|
||||
}
|
||||
return container.renderToBuffer(context: context)
|
||||
}
|
||||
return container.renderToBuffer(context: context)
|
||||
}
|
||||
|
||||
// MARK: - Container View
|
||||
|
||||
@@ -122,7 +122,7 @@ extension Panel where Footer == EmptyView {
|
||||
borderColor: borderColor,
|
||||
titleColor: titleColor,
|
||||
padding: padding,
|
||||
showFooterSeparator: true
|
||||
showFooterSeparator: false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ struct ViewBuilderTests {
|
||||
}
|
||||
|
||||
let views = buildViews()
|
||||
#expect(views is TupleView2<Text, Text>)
|
||||
#expect(views is TupleView<Text, Text>)
|
||||
}
|
||||
|
||||
@Test("ViewBuilder with three views")
|
||||
@@ -89,7 +89,7 @@ struct ViewBuilderTests {
|
||||
}
|
||||
|
||||
let views = buildViews()
|
||||
#expect(views is TupleView3<Text, Text, Text>)
|
||||
#expect(views is TupleView<Text, Text, Text>)
|
||||
}
|
||||
|
||||
@Test("VStack can contain views")
|
||||
|
||||
Reference in New Issue
Block a user