Simplify CellProvider protocol (#65)

In most of the cases users wouldn't need to create cell providers with more than one identifier or more than one section. We would like the most common cases to be as easy to use as possible, this PR renames `SimpleCellProvider` to `CellProvider` and old `CellProvider` to `IdentifiedCellProvider`. Old `Model` associated type is now renamed to `Section` with a default array-based sections type, while the new `Model` associated type allows setting the element type directly. This helps users avoid thinking about nested arrays in the cases where multiple `ListView` sections are not needed.

* Simplify CellProvider protocol
* Fix AppKitRenderer compilation issue
* Fix formatting
This commit is contained in:
Max Desiatov 2019-03-16 10:47:51 +00:00 committed by GitHub
parent fe43b9b9b0
commit ec695e36b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 43 additions and 41 deletions

View File

@ -617,7 +617,7 @@
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = TokamakDemoMac/TokamakDemoMac.entitlements;
CODE_SIGN_ENTITLEMENTS = "TokamakDemo-macOS/TokamakDemoMac.entitlements";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
@ -650,7 +650,7 @@
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = TokamakDemoMac/TokamakDemoMac.entitlements;
CODE_SIGN_ENTITLEMENTS = "TokamakDemo-macOS/TokamakDemoMac.entitlements";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;

View File

@ -33,7 +33,7 @@ extension ElementaryParticles: CustomStringConvertible {
var description: String { return rawValue.localizedCapitalized }
}
private struct Cells: SimpleCellProvider {
private struct Cells: CellProvider {
static func cell(
props: Null,
item: ElementaryParticles,
@ -52,7 +52,7 @@ private struct Cells: SimpleCellProvider {
typealias Props = Null
typealias Model = [[ElementaryParticles]]
typealias Model = ElementaryParticles
}
struct CollectionExample: PureLeafComponent {
@ -64,7 +64,7 @@ struct CollectionExample: PureLeafComponent {
Edges.equal(to: .parent, inset: 20),
backgroundColor: .white
),
model: [ElementaryParticles.allCases]
singleSection: ElementaryParticles.allCases
))
}
}

View File

@ -9,7 +9,7 @@
import Tokamak
private struct Cells: SimpleCellProvider {
private struct Cells: CellProvider {
static func cell(
props: Null,
item: AppRoute,
@ -27,7 +27,7 @@ private struct Cells: SimpleCellProvider {
typealias Props = Null
typealias Model = [[AppRoute]]
typealias Model = AppRoute
}
struct List: PureLeafComponent {

View File

@ -9,9 +9,9 @@
import Tokamak
struct ListProvider: SimpleCellProvider {
struct ListProvider: CellProvider {
typealias Props = Null
typealias Model = [[Int]]
typealias Model = Int
static func cell(props _: Null, item: Int, path _: CellPath) -> AnyNode {
return Label.node(.init(

View File

@ -8,7 +8,7 @@
public struct CollectionView<T: CellProvider>: HostComponent {
public struct Props: Equatable, StyleProps {
public let cellProps: T.Props
public let model: T.Model
public let sections: T.Sections
public let onSelect: Handler<CellPath>?
public let style: Style?
@ -16,10 +16,10 @@ public struct CollectionView<T: CellProvider>: HostComponent {
_ style: Style? = nil,
cellProps: T.Props,
onSelect: Handler<CellPath>? = nil,
singleSection: T.Model.Element
singleSection: T.Sections.Element
) {
self.cellProps = cellProps
model = T.Model.single(section: singleSection)
sections = T.Sections.single(section: singleSection)
self.onSelect = onSelect
self.style = style
}
@ -27,11 +27,11 @@ public struct CollectionView<T: CellProvider>: HostComponent {
public init(
_ style: Style? = nil,
cellProps: T.Props,
model: T.Model,
sections: T.Sections,
onSelect: Handler<CellPath>? = nil
) {
self.cellProps = cellProps
self.model = model
self.sections = sections
self.onSelect = onSelect
self.style = style
}
@ -43,11 +43,11 @@ public struct CollectionView<T: CellProvider>: HostComponent {
extension CollectionView.Props where T.Props == Null {
public init(
_ style: Style? = nil,
model: T.Model,
sections: T.Sections,
onSelect: Handler<CellPath>? = nil
) {
cellProps = Null()
self.model = model
self.sections = sections
self.onSelect = onSelect
self.style = style
}
@ -55,10 +55,10 @@ extension CollectionView.Props where T.Props == Null {
public init(
_ style: Style? = nil,
onSelect: Handler<CellPath>? = nil,
singleSection: T.Model.Element
singleSection: T.Sections.Element
) {
cellProps = Null()
model = T.Model.single(section: singleSection)
sections = T.Sections.single(section: singleSection)
self.onSelect = onSelect
self.style = style
}

View File

@ -33,15 +33,17 @@ extension Array: SectionedModel
}
}
public protocol CellProvider {
public protocol IdentifiedCellProvider {
associatedtype Props: Equatable
associatedtype Identifier: RawRepresentable, CaseIterable
where Identifier.RawValue == String
associatedtype Model: SectionedModel & Equatable
associatedtype Model: Equatable
associatedtype Sections: SectionedModel & Equatable = [[Model]] where
Sections.Element.Element == Model
static func cell(
props: Props,
item: Model.Element.Element,
item: Model,
path: CellPath
) -> (Identifier, AnyNode)
}
@ -50,19 +52,19 @@ public enum SingleIdentifier: String, CaseIterable {
case single
}
public protocol SimpleCellProvider: CellProvider
public protocol CellProvider: IdentifiedCellProvider
where Identifier == SingleIdentifier {
static func cell(
props: Props,
item: Model.Element.Element,
item: Model,
path: CellPath
) -> AnyNode
}
extension SimpleCellProvider {
extension CellProvider {
public static func cell(
props: Props,
item: Model.Element.Element,
item: Model,
path: CellPath
) -> (Identifier, AnyNode) {
return (.single, cell(props: props, item: item, path: path))
@ -72,7 +74,7 @@ extension SimpleCellProvider {
public struct ListView<T: CellProvider>: HostComponent {
public struct Props: Equatable, StyleProps {
public let cellProps: T.Props
public let model: T.Model
public let model: T.Sections
public let onSelect: Handler<CellPath>?
public let style: Style?
@ -80,10 +82,10 @@ public struct ListView<T: CellProvider>: HostComponent {
_ style: Style? = nil,
cellProps: T.Props,
onSelect: Handler<CellPath>? = nil,
singleSection: T.Model.Element
singleSection: T.Sections.Element
) {
self.cellProps = cellProps
model = T.Model.single(section: singleSection)
model = T.Sections.single(section: singleSection)
self.onSelect = onSelect
self.style = style
}
@ -91,7 +93,7 @@ public struct ListView<T: CellProvider>: HostComponent {
public init(
_ style: Style? = nil,
cellProps: T.Props,
model: T.Model,
model: T.Sections,
onSelect: Handler<CellPath>? = nil
) {
self.cellProps = cellProps
@ -107,7 +109,7 @@ public struct ListView<T: CellProvider>: HostComponent {
extension ListView.Props where T.Props == Null {
public init(
_ style: Style? = nil,
model: T.Model,
model: T.Sections,
onSelect: Handler<CellPath>? = nil
) {
cellProps = Null()
@ -119,10 +121,10 @@ extension ListView.Props where T.Props == Null {
public init(
_ style: Style? = nil,
onSelect: Handler<CellPath>? = nil,
singleSection: T.Model.Element
singleSection: T.Sections.Element
) {
cellProps = Null()
model = T.Model.single(section: singleSection)
model = T.Sections.single(section: singleSection)
self.onSelect = onSelect
self.style = style
}

View File

@ -21,7 +21,7 @@ import Tokamak
// let _collectionViewWitnessTableHack: NSHostComponent.Type =
// CollectionView<HackyProvider>.self
struct HackyProvider: SimpleCellProvider {
struct HackyProvider: CellProvider {
static func cell(
props: Props, item: Int, path: CellPath
) -> AnyNode {
@ -29,7 +29,7 @@ struct HackyProvider: SimpleCellProvider {
}
typealias Props = Null
typealias Model = [[Int]]
typealias Model = Int
}
class NSTarget: Target {

View File

@ -32,21 +32,21 @@ private final class DataSource<T: CellProvider>: NSObject,
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return props.model.count
return props.sections.count
}
func collectionView(
_ collectionView: UICollectionView,
numberOfItemsInSection section: Int
) -> Int {
return props.model[section].count
return props.sections[section].count
}
func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell {
let item = props.model[indexPath.section][indexPath.row]
let item = props.sections[indexPath.section][indexPath.row]
let (id, node) = T.cell(
props: props.cellProps,
@ -116,10 +116,10 @@ final class CollectionViewBox<T: CellProvider>: ViewBox<TokamakCollectionView> {
return dataSource.props
}
set {
let oldModel = dataSource.props.model
let oldSections = dataSource.props.sections
dataSource.props = newValue
delegate.onSelect = newValue.onSelect?.value
if oldModel != newValue.model {
if oldSections != newValue.sections {
view.reloadData()
}
}

View File

@ -19,7 +19,7 @@ let _listViewWitnessTableHack: UIHostComponent.Type =
let _collectionViewWitnessTableHack: UIHostComponent.Type =
CollectionView<HackyProvider>.self
struct HackyProvider: SimpleCellProvider {
struct HackyProvider: CellProvider {
static func cell(
props: Props, item: Int, path: CellPath
) -> AnyNode {
@ -27,7 +27,7 @@ struct HackyProvider: SimpleCellProvider {
}
typealias Props = Null
typealias Model = [[Int]]
typealias Model = Int
}
class UITarget: Target {