-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
448 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// | ||
// Element+HeaderFooter.swift | ||
// BlueprintUILists | ||
// | ||
// Created by Kyle Van Essen on 7/24/22. | ||
// | ||
|
||
import BlueprintUI | ||
import ListableUI | ||
|
||
|
||
// MARK: HeaderFooter / HeaderFooterContent Extensions | ||
|
||
|
||
extension Element { | ||
|
||
/// Converts the given `Element` into a Listable `HeaderFooter`. You many also optionally | ||
/// configure the header / footer, setting its values such as the `onTap` callbacks, etc. | ||
/// | ||
/// ```swift | ||
/// MyElement(...) | ||
/// .headerFooter { header in | ||
/// header.onTap = { ... } | ||
/// } | ||
/// ``` | ||
/// | ||
/// ## ⚠️ Performance Considerations | ||
/// Unless your `Element` conforms to `Equatable` or `IsEquivalentContent`, | ||
/// it will return `false` for `isEquivalent` for each content update, which can dramatically | ||
/// hurt performance for longer lists (eg, more than 20 items): it will be re-measured for each content update. | ||
/// | ||
/// It is encouraged for these longer lists, you ensure your `Element` conforms to one of these protocols. | ||
public func headerFooter( | ||
configure : (inout HeaderFooter<WrappedHeaderFooterContent<Self>>) -> () = { _ in } | ||
) -> HeaderFooter<WrappedHeaderFooterContent<Self>> { | ||
HeaderFooter( | ||
WrappedHeaderFooterContent( | ||
represented: self | ||
), | ||
configure: configure | ||
) | ||
} | ||
} | ||
|
||
|
||
public struct WrappedHeaderFooterContent<ElementType:Element> : BlueprintHeaderFooterContent | ||
{ | ||
public let represented : ElementType | ||
|
||
public func isEquivalent(to other: Self) -> Bool { | ||
false | ||
} | ||
|
||
public var elementRepresentation: Element { | ||
represented | ||
} | ||
} | ||
|
||
|
||
extension WrappedHeaderFooterContent where ElementType : Equatable { | ||
|
||
public func isEquivalent(to other: Self) -> Bool { | ||
represented == other.represented | ||
} | ||
|
||
public var reappliesToVisibleView: ReappliesToVisibleView { | ||
.ifNotEquivalent | ||
} | ||
} | ||
|
||
|
||
extension WrappedHeaderFooterContent where ElementType : IsEquivalentContent { | ||
|
||
public func isEquivalent(to other: Self) -> Bool { | ||
represented.isEquivalent(to: other.represented) | ||
} | ||
|
||
public var reappliesToVisibleView: ReappliesToVisibleView { | ||
.ifNotEquivalent | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// | ||
// Element+Item.swift | ||
// BlueprintUILists | ||
// | ||
// Created by Kyle Van Essen on 7/24/22. | ||
// | ||
|
||
import BlueprintUI | ||
import ListableUI | ||
|
||
|
||
// MARK: Item / ItemContent Extensions | ||
|
||
extension Element { | ||
|
||
/// Converts the given `Element` into a Listable `Item`. You many also optionally | ||
/// configure the item, setting its values such as the `onDisplay` callbacks, etc. | ||
/// | ||
/// ```swift | ||
/// MyElement(...) | ||
/// .item { item in | ||
/// item.insertAndRemoveAnimations = .scaleUp | ||
/// } | ||
/// ``` | ||
/// | ||
/// ## ⚠️ Performance Considerations | ||
/// Unless your `Element` conforms to `Equatable` or `IsEquivalentContent`, | ||
/// it will return `false` for `isEquivalent` for each content update, which can dramatically | ||
/// hurt performance for longer lists (eg, more than 20 items): it will be re-measured for each content update. | ||
/// | ||
/// It is encouraged for these longer lists, you ensure your `Element` conforms to one of these protocols. | ||
public func item( | ||
configure : (inout Item<WrappedElementContent<Self, ObjectIdentifier>>) -> () = { _ in } | ||
) -> Item<WrappedElementContent<Self, ObjectIdentifier>> { | ||
Item( | ||
WrappedElementContent( | ||
represented: self, | ||
identifierValue: ObjectIdentifier(Self.Type.self) | ||
), | ||
configure: configure | ||
) | ||
} | ||
|
||
/// Converts the given `Element` into a Listable `Item` with the provided ID. You can use this ID | ||
/// to scroll to or later access the item through the regular list access APIs. | ||
/// You many also optionally configure the item, setting its values such as the `onDisplay` callbacks, etc. | ||
/// | ||
/// ```swift | ||
/// MyElement(...) | ||
/// .item(id: "my-provided-id") { item in | ||
/// item.insertAndRemoveAnimations = .scaleUp | ||
/// } | ||
/// ``` | ||
/// | ||
/// ## ⚠️ Performance Considerations | ||
/// Unless your `Element` conforms to `Equatable` or `IsEquivalentContent`, | ||
/// it will return `false` for `isEquivalent` for each content update, which can dramatically | ||
/// hurt performance for longer lists (eg, more than 20 items): it will be re-measured for each content update. | ||
/// | ||
/// It is encouraged for these longer lists, you ensure your `Element` conforms to one of these protocols. | ||
public func item<ID:Hashable>( | ||
id : ID, | ||
configure : (inout Item<WrappedElementContent<Self, ID>>) -> () = { _ in } | ||
) -> Item<WrappedElementContent<Self, ID>> { | ||
Item( | ||
WrappedElementContent( | ||
represented: self, | ||
identifierValue: id | ||
), | ||
configure: configure | ||
) | ||
} | ||
|
||
/// Used by internal Listable methods to convert type-erased `Element` instances into `Item` instances. | ||
func toAnyItemConvertible() -> AnyItemConvertible { | ||
/// We use `type(of:)` to ensure we get the actual type, not just `Element`. | ||
WrappedElementContent( | ||
represented: self, | ||
identifierValue: ObjectIdentifier(type(of: self)) | ||
) | ||
} | ||
} | ||
|
||
|
||
public struct WrappedElementContent<ElementType:Element, IdentifierValue:Hashable> : BlueprintItemContent | ||
{ | ||
public let represented : ElementType | ||
|
||
public let identifierValue: IdentifierValue | ||
|
||
public func isEquivalent(to other: Self) -> Bool { | ||
false | ||
} | ||
|
||
public func element(with info: ApplyItemContentInfo) -> Element { | ||
represented | ||
} | ||
} | ||
|
||
|
||
extension WrappedElementContent where ElementType : Equatable { | ||
|
||
public func isEquivalent(to other: Self) -> Bool { | ||
represented == other.represented | ||
} | ||
|
||
public var reappliesToVisibleView: ReappliesToVisibleView { | ||
.ifNotEquivalent | ||
} | ||
} | ||
|
||
|
||
extension WrappedElementContent where ElementType : IsEquivalentContent { | ||
|
||
public func isEquivalent(to other: Self) -> Bool { | ||
represented.isEquivalent(to: other.represented) | ||
} | ||
|
||
public var reappliesToVisibleView: ReappliesToVisibleView { | ||
.ifNotEquivalent | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// | ||
// ListableBuilder+Element.swift | ||
// BlueprintUILists | ||
// | ||
// Created by Kyle Van Essen on 7/24/22. | ||
// | ||
|
||
import BlueprintUI | ||
import ListableUI | ||
|
||
|
||
/// Adds `Element` support when building `AnyItemConvertible` arrays, which allows: | ||
/// | ||
/// ```swift | ||
/// Section("3") { section in | ||
/// TestContent1() // An ItemContent | ||
/// | ||
/// Element1() // An Element | ||
/// Element2() // An Element | ||
/// } | ||
/// ``` | ||
/// | ||
/// ## Note | ||
/// Takes advantage of `@_disfavoredOverload` to avoid ambiguous method resolution with the default implementations. | ||
/// See more here: https://github.com/apple/swift/blob/main/docs/ReferenceGuides/UnderscoredAttributes.md#_disfavoredoverload | ||
/// | ||
public extension ListableBuilder where ContentType == AnyItemConvertible { | ||
|
||
/// Required by every result builder to build combined results from statement blocks. | ||
@_disfavoredOverload static func buildBlock(_ components: [Element]...) -> Component { | ||
components.reduce(into: []) { $0 += $1.map { $0.toAnyItemConvertible() } } | ||
} | ||
|
||
/// If declared, provides contextual type information for statement expressions to translate them into partial results. | ||
@_disfavoredOverload static func buildExpression(_ expression: Element) -> Component { | ||
[expression.toAnyItemConvertible()] | ||
} | ||
|
||
/// If declared, provides contextual type information for statement expressions to translate them into partial results. | ||
@_disfavoredOverload static func buildExpression(_ expression: [Element]) -> Component { | ||
expression.map { $0.toAnyItemConvertible() } | ||
} | ||
|
||
/// Enables support for `if` statements that do not have an `else`. | ||
@_disfavoredOverload static func buildOptional(_ component: [Element]?) -> Component { | ||
component?.map { $0.toAnyItemConvertible() } ?? [] | ||
} | ||
|
||
/// With buildEither(second:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result. | ||
@_disfavoredOverload static func buildEither(first component: [Element]) -> Component { | ||
component.map { $0.toAnyItemConvertible() } | ||
} | ||
|
||
/// With buildEither(first:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result. | ||
@_disfavoredOverload static func buildEither(second component: [Element]) -> Component { | ||
component.map { $0.toAnyItemConvertible() } | ||
} | ||
|
||
/// Enables support for 'for..in' loops by combining the results of all iterations into a single result. | ||
@_disfavoredOverload static func buildArray(_ components: [[Element]]) -> Component { | ||
components.flatMap { $0.map { $0.toAnyItemConvertible() } } | ||
} | ||
|
||
/// If declared, this will be called on the partial result of an `if #available` block to allow the result builder to erase type information. | ||
@_disfavoredOverload static func buildLimitedAvailability(_ component: [Element]) -> Component { | ||
component.map { $0.toAnyItemConvertible() } | ||
} | ||
|
||
/// If declared, this will be called on the partial result from the outermost block statement to produce the final returned result. | ||
@_disfavoredOverload static func buildFinalResult(_ component: [Element]) -> FinalResult { | ||
component.map { $0.toAnyItemConvertible() } | ||
} | ||
} | ||
|
Oops, something went wrong.