Skip to content

Commit 9a735a3

Browse files
committed
more
1 parent 16ce47b commit 9a735a3

10 files changed

+448
-88
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//
2+
// Element+HeaderFooter.swift
3+
// BlueprintUILists
4+
//
5+
// Created by Kyle Van Essen on 7/24/22.
6+
//
7+
8+
import BlueprintUI
9+
import ListableUI
10+
11+
12+
// MARK: HeaderFooter / HeaderFooterContent Extensions
13+
14+
15+
extension Element {
16+
17+
/// Converts the given `Element` into a Listable `HeaderFooter`. You many also optionally
18+
/// configure the header / footer, setting its values such as the `onTap` callbacks, etc.
19+
///
20+
/// ```swift
21+
/// MyElement(...)
22+
/// .headerFooter { header in
23+
/// header.onTap = { ... }
24+
/// }
25+
/// ```
26+
///
27+
/// ## ⚠️ Performance Considerations
28+
/// Unless your `Element` conforms to `Equatable` or `IsEquivalentContent`,
29+
/// it will return `false` for `isEquivalent` for each content update, which can dramatically
30+
/// hurt performance for longer lists (eg, more than 20 items): it will be re-measured for each content update.
31+
///
32+
/// It is encouraged for these longer lists, you ensure your `Element` conforms to one of these protocols.
33+
public func headerFooter(
34+
configure : (inout HeaderFooter<WrappedHeaderFooterContent<Self>>) -> () = { _ in }
35+
) -> HeaderFooter<WrappedHeaderFooterContent<Self>> {
36+
HeaderFooter(
37+
WrappedHeaderFooterContent(
38+
represented: self
39+
),
40+
configure: configure
41+
)
42+
}
43+
}
44+
45+
46+
public struct WrappedHeaderFooterContent<ElementType:Element> : BlueprintHeaderFooterContent
47+
{
48+
public let represented : ElementType
49+
50+
public func isEquivalent(to other: Self) -> Bool {
51+
false
52+
}
53+
54+
public var elementRepresentation: Element {
55+
represented
56+
}
57+
}
58+
59+
60+
extension WrappedHeaderFooterContent where ElementType : Equatable {
61+
62+
public func isEquivalent(to other: Self) -> Bool {
63+
represented == other.represented
64+
}
65+
66+
public var reappliesToVisibleView: ReappliesToVisibleView {
67+
.ifNotEquivalent
68+
}
69+
}
70+
71+
72+
extension WrappedHeaderFooterContent where ElementType : IsEquivalentContent {
73+
74+
public func isEquivalent(to other: Self) -> Bool {
75+
represented.isEquivalent(to: other.represented)
76+
}
77+
78+
public var reappliesToVisibleView: ReappliesToVisibleView {
79+
.ifNotEquivalent
80+
}
81+
}
+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//
2+
// Element+Item.swift
3+
// BlueprintUILists
4+
//
5+
// Created by Kyle Van Essen on 7/24/22.
6+
//
7+
8+
import BlueprintUI
9+
import ListableUI
10+
11+
12+
// MARK: Item / ItemContent Extensions
13+
14+
extension Element {
15+
16+
/// Converts the given `Element` into a Listable `Item`. You many also optionally
17+
/// configure the item, setting its values such as the `onDisplay` callbacks, etc.
18+
///
19+
/// ```swift
20+
/// MyElement(...)
21+
/// .item { item in
22+
/// item.insertAndRemoveAnimations = .scaleUp
23+
/// }
24+
/// ```
25+
///
26+
/// ## ⚠️ Performance Considerations
27+
/// Unless your `Element` conforms to `Equatable` or `IsEquivalentContent`,
28+
/// it will return `false` for `isEquivalent` for each content update, which can dramatically
29+
/// hurt performance for longer lists (eg, more than 20 items): it will be re-measured for each content update.
30+
///
31+
/// It is encouraged for these longer lists, you ensure your `Element` conforms to one of these protocols.
32+
public func item(
33+
configure : (inout Item<WrappedElementContent<Self, ObjectIdentifier>>) -> () = { _ in }
34+
) -> Item<WrappedElementContent<Self, ObjectIdentifier>> {
35+
Item(
36+
WrappedElementContent(
37+
represented: self,
38+
identifierValue: ObjectIdentifier(Self.Type.self)
39+
),
40+
configure: configure
41+
)
42+
}
43+
44+
/// Converts the given `Element` into a Listable `Item` with the provided ID. You can use this ID
45+
/// to scroll to or later access the item through the regular list access APIs.
46+
/// You many also optionally configure the item, setting its values such as the `onDisplay` callbacks, etc.
47+
///
48+
/// ```swift
49+
/// MyElement(...)
50+
/// .item(id: "my-provided-id") { item in
51+
/// item.insertAndRemoveAnimations = .scaleUp
52+
/// }
53+
/// ```
54+
///
55+
/// ## ⚠️ Performance Considerations
56+
/// Unless your `Element` conforms to `Equatable` or `IsEquivalentContent`,
57+
/// it will return `false` for `isEquivalent` for each content update, which can dramatically
58+
/// hurt performance for longer lists (eg, more than 20 items): it will be re-measured for each content update.
59+
///
60+
/// It is encouraged for these longer lists, you ensure your `Element` conforms to one of these protocols.
61+
public func item<ID:Hashable>(
62+
id : ID,
63+
configure : (inout Item<WrappedElementContent<Self, ID>>) -> () = { _ in }
64+
) -> Item<WrappedElementContent<Self, ID>> {
65+
Item(
66+
WrappedElementContent(
67+
represented: self,
68+
identifierValue: id
69+
),
70+
configure: configure
71+
)
72+
}
73+
74+
/// Used by internal Listable methods to convert type-erased `Element` instances into `Item` instances.
75+
func toAnyItemConvertible() -> AnyItemConvertible {
76+
/// We use `type(of:)` to ensure we get the actual type, not just `Element`.
77+
WrappedElementContent(
78+
represented: self,
79+
identifierValue: ObjectIdentifier(type(of: self))
80+
)
81+
}
82+
}
83+
84+
85+
public struct WrappedElementContent<ElementType:Element, IdentifierValue:Hashable> : BlueprintItemContent
86+
{
87+
public let represented : ElementType
88+
89+
public let identifierValue: IdentifierValue
90+
91+
public func isEquivalent(to other: Self) -> Bool {
92+
false
93+
}
94+
95+
public func element(with info: ApplyItemContentInfo) -> Element {
96+
represented
97+
}
98+
}
99+
100+
101+
extension WrappedElementContent where ElementType : Equatable {
102+
103+
public func isEquivalent(to other: Self) -> Bool {
104+
represented == other.represented
105+
}
106+
107+
public var reappliesToVisibleView: ReappliesToVisibleView {
108+
.ifNotEquivalent
109+
}
110+
}
111+
112+
113+
extension WrappedElementContent where ElementType : IsEquivalentContent {
114+
115+
public func isEquivalent(to other: Self) -> Bool {
116+
represented.isEquivalent(to: other.represented)
117+
}
118+
119+
public var reappliesToVisibleView: ReappliesToVisibleView {
120+
.ifNotEquivalent
121+
}
122+
}

BlueprintUILists/Sources/Element.swift

-68
This file was deleted.

BlueprintUILists/Sources/HeaderFooter.swift BlueprintUILists/Sources/ElementHeaderFooter.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// HeaderFooter.swift
2+
// ElementHeaderFooter.swift
33
// BlueprintUILists
44
//
55
// Created by Kyle Van Essen on 10/9/20.
@@ -9,6 +9,8 @@ import ListableUI
99
import BlueprintUI
1010

1111

12+
///
13+
/// ⚠️ This method is soft-deprecated! Consider using `myElement.headerFooter(...)` instead.
1214
///
1315
/// Provides a way to create a `HeaderFooter` for your Blueprint elements without
1416
/// requiring the creation of a new `BlueprintHeaderFooterContent` struct.
@@ -62,6 +64,8 @@ public func ElementHeaderFooter<Represented>(
6264
)
6365
}
6466

67+
///
68+
/// ⚠️ This method is soft-deprecated! Consider using `myElement.headerFooter(...)` instead.
6569
///
6670
/// Provides a way to create a `HeaderFooter` for your Blueprint elements without
6771
/// requiring the creation of a new `BlueprintHeaderFooterContent` struct.

BlueprintUILists/Sources/Item.swift BlueprintUILists/Sources/ElementItem.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Item.swift
2+
// ElementItem.swift
33
// BlueprintUILists
44
//
55
// Created by Kyle Van Essen on 9/10/20.
@@ -9,6 +9,8 @@ import ListableUI
99
import BlueprintUI
1010

1111

12+
///
13+
/// ⚠️ This method is soft-deprecated! Consider using `myElement.item(...)` instead.
1214
///
1315
/// Provides a way to create an `Item` for your Blueprint elements without
1416
/// requiring the creation of a new `BlueprintItemContent` struct.
@@ -68,6 +70,8 @@ public func ElementItem<Represented, IdentifierValue:Hashable>(
6870

6971

7072
///
73+
/// ⚠️ This method is soft-deprecated! Consider using `myElement.item(...)` instead.
74+
///
7175
/// Provides a way to create an `Item` for your Blueprint elements without
7276
/// requiring the creation of a new `BlueprintItemContent` struct.
7377
///
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// ListableBuilder+Element.swift
3+
// BlueprintUILists
4+
//
5+
// Created by Kyle Van Essen on 7/24/22.
6+
//
7+
8+
import BlueprintUI
9+
import ListableUI
10+
11+
12+
/// Adds `Element` support when building `AnyItemConvertible` arrays, which allows:
13+
///
14+
/// ```swift
15+
/// Section("3") { section in
16+
/// TestContent1() // An ItemContent
17+
///
18+
/// Element1() // An Element
19+
/// Element2() // An Element
20+
/// }
21+
/// ```
22+
///
23+
/// ## Note
24+
/// Takes advantage of `@_disfavoredOverload` to avoid ambiguous method resolution with the default implementations.
25+
/// See more here: https://github.com/apple/swift/blob/main/docs/ReferenceGuides/UnderscoredAttributes.md#_disfavoredoverload
26+
///
27+
public extension ListableBuilder where ContentType == AnyItemConvertible {
28+
29+
/// Required by every result builder to build combined results from statement blocks.
30+
@_disfavoredOverload static func buildBlock(_ components: [Element]...) -> Component {
31+
components.reduce(into: []) { $0 += $1.map { $0.toAnyItemConvertible() } }
32+
}
33+
34+
/// If declared, provides contextual type information for statement expressions to translate them into partial results.
35+
@_disfavoredOverload static func buildExpression(_ expression: Element) -> Component {
36+
[expression.toAnyItemConvertible()]
37+
}
38+
39+
/// If declared, provides contextual type information for statement expressions to translate them into partial results.
40+
@_disfavoredOverload static func buildExpression(_ expression: [Element]) -> Component {
41+
expression.map { $0.toAnyItemConvertible() }
42+
}
43+
44+
/// Enables support for `if` statements that do not have an `else`.
45+
@_disfavoredOverload static func buildOptional(_ component: [Element]?) -> Component {
46+
component?.map { $0.toAnyItemConvertible() } ?? []
47+
}
48+
49+
/// With buildEither(second:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
50+
@_disfavoredOverload static func buildEither(first component: [Element]) -> Component {
51+
component.map { $0.toAnyItemConvertible() }
52+
}
53+
54+
/// With buildEither(first:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
55+
@_disfavoredOverload static func buildEither(second component: [Element]) -> Component {
56+
component.map { $0.toAnyItemConvertible() }
57+
}
58+
59+
/// Enables support for 'for..in' loops by combining the results of all iterations into a single result.
60+
@_disfavoredOverload static func buildArray(_ components: [[Element]]) -> Component {
61+
components.flatMap { $0.map { $0.toAnyItemConvertible() } }
62+
}
63+
64+
/// If declared, this will be called on the partial result of an `if #available` block to allow the result builder to erase type information.
65+
@_disfavoredOverload static func buildLimitedAvailability(_ component: [Element]) -> Component {
66+
component.map { $0.toAnyItemConvertible() }
67+
}
68+
69+
/// If declared, this will be called on the partial result from the outermost block statement to produce the final returned result.
70+
@_disfavoredOverload static func buildFinalResult(_ component: [Element]) -> FinalResult {
71+
component.map { $0.toAnyItemConvertible() }
72+
}
73+
}
74+

0 commit comments

Comments
 (0)