Skip to content

Commit

Permalink
more
Browse files Browse the repository at this point in the history
  • Loading branch information
kyleve committed Jul 25, 2022
1 parent 16ce47b commit 9a735a3
Show file tree
Hide file tree
Showing 10 changed files with 448 additions and 88 deletions.
81 changes: 81 additions & 0 deletions BlueprintUILists/Sources/Element+HeaderFooter.swift
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
}
}
122 changes: 122 additions & 0 deletions BlueprintUILists/Sources/Element+Item.swift
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
}
}
68 changes: 0 additions & 68 deletions BlueprintUILists/Sources/Element.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// HeaderFooter.swift
// ElementHeaderFooter.swift
// BlueprintUILists
//
// Created by Kyle Van Essen on 10/9/20.
Expand All @@ -9,6 +9,8 @@ import ListableUI
import BlueprintUI


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

///
/// ⚠️ This method is soft-deprecated! Consider using `myElement.headerFooter(...)` instead.
///
/// Provides a way to create a `HeaderFooter` for your Blueprint elements without
/// requiring the creation of a new `BlueprintHeaderFooterContent` struct.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Item.swift
// ElementItem.swift
// BlueprintUILists
//
// Created by Kyle Van Essen on 9/10/20.
Expand All @@ -9,6 +9,8 @@ import ListableUI
import BlueprintUI


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


///
/// ⚠️ This method is soft-deprecated! Consider using `myElement.item(...)` instead.
///
/// Provides a way to create an `Item` for your Blueprint elements without
/// requiring the creation of a new `BlueprintItemContent` struct.
///
Expand Down
74 changes: 74 additions & 0 deletions BlueprintUILists/Sources/ListableBuilder+Element.swift
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() }
}
}

Loading

0 comments on commit 9a735a3

Please sign in to comment.