Skip to content

Commit df6b03a

Browse files
authored
Merge pull request #1360 from liveview-native/custom-element-guides
Update addon documentation
2 parents 3f33641 + cc81a33 commit df6b03a

File tree

3 files changed

+455
-32
lines changed

3 files changed

+455
-32
lines changed

Sources/LiveViewNative/Live/LiveElement.swift

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,67 @@
88
import SwiftUI
99
import LiveViewNativeCore
1010

11+
/// Describes a View that is used as an element in a LiveView Native template.
12+
///
13+
/// Any properties of the ``SwiftUI/View`` will be decoded from element attributes.
14+
///
15+
/// - Precondition: A generic argument named `Root` conforming to ``RootRegistry`` must be included on the struct.
16+
/// - Precondition: All properties should have a default value, or be marked optional.
17+
///
18+
/// ```swift
19+
/// @LiveElement
20+
/// struct MyTag<Root: RootRegistry>: View {
21+
/// private var label: String?
22+
/// private var count: Int = 0
23+
///
24+
/// var body: some View {
25+
/// Text(label ?? "")
26+
/// Text("Value: \(count)")
27+
/// }
28+
/// }
29+
/// ```
30+
///
31+
/// ```html
32+
/// <MyTag label="Cookies" count="3" />
33+
/// ```
34+
///
35+
/// By default, the property name is used verbatim as the attribute name.
36+
/// Use the ``LiveAttribute(_:)`` macro to customize the name of an attribute.
37+
///
38+
/// Use the ``LiveElementIgnored()`` macro to disable decoding of a property.
39+
/// This is useful when interfacing with SwiftUI property wrappers, such as ``SwiftUI/State`` and ``SwiftUI/Environment``.
1140
@attached(member, names: named(liveElement), named(_TrackedContent))
1241
@attached(memberAttribute)
1342
public macro LiveElement() = #externalMacro(module: "LiveViewNativeMacros", type: "LiveElementMacro")
1443

44+
/// Marks a property as an attribute on a ``LiveElement()``.
45+
///
46+
/// By default, the property name is used verbatim as the attribute name.
47+
/// Provide the `name` argument to customize the namespace/name of the attribute.
48+
///
49+
/// ```swift
50+
/// @LiveAttribute(.init(namespace: "item", name: "count"))
51+
/// private var count: Int = 0
52+
/// ```
53+
///
54+
/// ```html
55+
/// <MyTag item:count="3" />
56+
/// ```
1557
@attached(accessor, names: named(get))
1658
public macro LiveAttribute(_ name: AttributeName? = nil) = #externalMacro(module: "LiveViewNativeMacros", type: "LiveAttributeMacro")
1759

60+
/// Disables attribute decoding for a property on a ``LiveElement()``.
61+
///
62+
/// By default, all properties are treated as attributes by ``LiveElement()``.
63+
/// This macro disables attribute decoding for a property.
64+
///
65+
/// This can used with SwiftUI property wrappers, such as ``SwiftUI/State`` and ``SwiftUI/Environment``,
66+
/// which should only be handled client-side.
67+
///
68+
/// ```swift
69+
/// @LiveElementIgnored
70+
/// @State private var isExpanded = false
71+
/// ```
1872
@attached(accessor, names: named(willSet))
1973
public macro LiveElementIgnored() = #externalMacro(module: "LiveViewNativeMacros", type: "LiveElementIgnoredMacro")
2074

@@ -24,7 +78,9 @@ public protocol _LiveElementTrackedContent {
2478
}
2579

2680
@propertyWrapper public struct _LiveElementTracked<R: RootRegistry, T: _LiveElementTrackedContent>: DynamicProperty {
81+
/// The ``ElementNode`` used to build a live element.
2782
@ObservedElement public var element
83+
/// The ``LiveContext`` for a live element.
2884
@LiveContext<R> public var context
2985

3086
public var wrappedValue: T
@@ -52,12 +108,22 @@ public protocol _LiveElementTrackedContent {
52108
}
53109

54110
public extension _LiveElementTracked {
111+
/// Builds all child elements into a View hierarchy.
112+
///
113+
/// By default, elements with a `template` are filtered out.
114+
/// You can provide a custom `predicate` to filter different elements.
115+
///
116+
/// - Parameter predicate: The filter used to select child nodes for render.
55117
func children(_ predicate: (Node) -> Bool = { node in
56118
!node.attributes.contains(where: { $0.name.namespace == nil && $0.name.name == "template" })
57119
}) -> some View {
58120
context.coordinator.builder.fromNodes(_element.children.filter(predicate), context: context.storage)
59121
}
60122

123+
/// Builds all children with a given `template` attribute.
124+
///
125+
/// - Parameter template: The ``Template`` used to filter child elements. A String literal can be provided for simple template values.
126+
/// - Parameter includeDefault: Whether elements without a `template` attribute should be included in the filter. Defaults to `false`.
61127
func children(in template: Template, default includeDefault: Bool = false) -> some View {
62128
children {
63129
$0.attributes.contains(where: {
@@ -66,6 +132,10 @@ public extension _LiveElementTracked {
66132
}
67133
}
68134

135+
/// Check whether one or more children have a matching `template` attribute.
136+
///
137+
/// - Parameter template: The ``Template`` used to filter child elements. A String literal can be provided for simple template values.
138+
/// - Parameter includeDefault: Whether elements without a `template` attribute should be included in the filter. Defaults to `false`.
69139
func hasTemplate(_ template: Template, default includeDefault: Bool = false) -> Bool {
70140
_element.children.contains(where: {
71141
for attribute in $0.attributes {
@@ -83,11 +153,33 @@ public extension _LiveElementTracked {
83153
hasTemplate(.init(name, value: value))
84154
}
85155

156+
/// Array containing children of the ``ElementNode``.
86157
var childNodes: [LiveViewNativeCore.Node] {
87158
_element.children
88159
}
89160
}
90161

162+
/// A value passed to the `template` attribute, used for filtering children.
163+
///
164+
/// A string literal can be used to create a simple template.
165+
///
166+
/// ```swift
167+
/// $liveElement.children(in: "label")
168+
/// ```
169+
///
170+
/// ```html
171+
/// <Element template="label" />
172+
/// ```
173+
///
174+
/// When a `value` is passed, the `template` attribute should match the scheme `name.value`.
175+
///
176+
/// ```swift
177+
/// $liveElement.children(in: Template("phase", value: "success"))
178+
/// ```
179+
///
180+
/// ```html
181+
/// <Element template="phase.success" />
182+
/// ```
91183
public struct Template: RawRepresentable, ExpressibleByStringLiteral {
92184
let name: String
93185
let value: String?

0 commit comments

Comments
 (0)