Files
swift-cli-doc/Sources/CliDocCore/Nodes/LabeledContent.swift
Michael Housh c6a269f062
All checks were successful
CI / Run tests. (push) Successful in 52s
feat: Adds stack separators, removes some unused nodes from cli-doc module
2024-12-08 10:58:03 -05:00

149 lines
3.7 KiB
Swift

/// A text node that consists of a label and content.
///
///
public struct LabeledContent<Label: TextNode, Content: TextNode>: TextNode {
@usableFromInline
let label: Label
@usableFromInline
let content: Content
/// Create a new labeled content text node.
///
/// - Parameters:
/// - content: The content portion of the labeled content.
/// - label: The label for the content.
@inlinable
public init(
@TextBuilder _ content: () -> Content,
@TextBuilder label: () -> Label
) {
self.label = label()
self.content = content()
}
@inlinable
public var body: some TextNode {
style(.default)
}
}
public extension LabeledContent {
/// Apply the given style to the labeled content.
///
/// - Parameters:
/// - style: The labeled content style to apply.
@inlinable
func style<S: LabeledContentStyle>(_ style: S) -> some TextNode {
style.render(content: .init(label: label, content: content))
}
}
/// Holds the type-erased label and content of a ``LabeledContent`` text node.
///
/// This is used when creating custom styles for the ``LabeledContent``.
///
public struct LabeledContentConfiguration {
/// The type-erased label text node.
public let label: any TextNode
/// The type-erased content text node.
public let content: any TextNode
@usableFromInline
init(label: any TextNode, content: any TextNode) {
self.label = label
self.content = content
}
}
// MARK: - Style
/// Represents a style for ``LabeledContent``.
///
///
public protocol LabeledContentStyle: TextModifier where Content == LabeledContentConfiguration {}
public extension LabeledContentStyle where Self == HorizontalLabeledContentStyle {
/// The default labeled content style, which places the label
/// and content inline with a space as a separator.
///
static var `default`: Self {
horizontal()
}
/// A horizontal labeled content style, which places the label
/// and content inline with the given separator.
///
/// - Parameters:
/// - separator: The horizontal separator to use.
@inlinable
static func horizontal(separator: Separator.Horizontal = .space()) -> Self {
HorizontalLabeledContentStyle(separator: separator)
}
}
public extension LabeledContentStyle where Self == VerticalLabeledContentStyle {
/// A vertical labeled content style, which places the label
/// and content with the given vertical separator.
///
/// - Parameters:
/// - separator: The vertical separator to use.
@inlinable
static func vertical(separator: Separator.Vertical = .newLine()) -> Self {
VerticalLabeledContentStyle(separator: separator)
}
}
/// A labeled content style which places items inline based on a given
/// horizontal separator.
///
/// - See Also: ``LabeledContentStyle/horizontal(separator:)``
///
public struct HorizontalLabeledContentStyle: LabeledContentStyle {
@usableFromInline
let separator: Separator.Horizontal
@usableFromInline
init(separator: Separator.Horizontal) {
self.separator = separator
}
public func render(content: LabeledContentConfiguration) -> some TextNode {
HStack {
content.label
content.content
}
.separator(separator)
}
}
/// A labeled content style which places items based on a given
/// vertical separator.
///
/// - See Also: ``LabeledContentStyle/vertical(separator:)``
///
public struct VerticalLabeledContentStyle: LabeledContentStyle {
@usableFromInline
let separator: Separator.Vertical
@usableFromInline
init(separator: Separator.Vertical) {
self.separator = separator
}
public func render(content: LabeledContentConfiguration) -> some TextNode {
VStack {
content.label
content.content
}
.separator(separator)
}
}