diff --git a/Sources/Elementary/Core/Group.swift b/Sources/Elementary/Core/Group.swift new file mode 100644 index 0000000..8c5e3bb --- /dev/null +++ b/Sources/Elementary/Core/Group.swift @@ -0,0 +1,43 @@ +/// A container that groups HTML content without introducing any additional HTML tags. +/// +/// This type is useful when you want to return multiple sibling nodes from a single `some HTML` +/// context without adding an extra wrapper element. +/// +/// - Note: In Embedded mode, the HTML builder currently supports up to **6 direct child views** +/// in a single block (because variadic generics aren't available there yet). You can exceed +/// that limit by nesting content inside one or more `Group` blocks. +public struct Group: HTML { + public typealias Tag = Never + public typealias Body = Never + public typealias Content = Content + + public let content: Content + + @inlinable + public init(@HTMLBuilder content: () -> Content) { + self.content = content() + } + + @inlinable + public static func _render( + _ html: consuming Self, + into renderer: inout Renderer, + with context: consuming _RenderingContext + ) { + context.assertNoAttributes(self) + Content._render(html.content, into: &renderer, with: context) + } + + @inlinable + @_unavailableInEmbedded + public static func _render( + _ html: consuming Self, + into renderer: inout Renderer, + with context: consuming _RenderingContext + ) async throws { + context.assertNoAttributes(self) + try await Content._render(html.content, into: &renderer, with: context) + } +} + +extension Group: Sendable where Content: Sendable {} diff --git a/Tests/ElementaryTests/TagRenderingTests.swift b/Tests/ElementaryTests/TagRenderingTests.swift index 6e11a15..1070eca 100644 --- a/Tests/ElementaryTests/TagRenderingTests.swift +++ b/Tests/ElementaryTests/TagRenderingTests.swift @@ -33,6 +33,24 @@ final class TagRenderingTests: XCTestCase { ) } + func testRendersGroup() async throws { + try await HTMLAssertEqual( + div { + Group { + h1 {} + Group { + if true { + p {} + p {} + } + } + h1 {} + } + }, + "

" + ) + } + func testRendersOptionals() async throws { try await HTMLAssertEqual( div {