Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 0 additions & 257 deletions Sources/CollectionView/CollectionView.swift
Original file line number Diff line number Diff line change
@@ -1,263 +1,6 @@
import IndexedCollection
import SwiftUI

public enum CollectionViewListDirection {
case vertical
case horizontal
}

/**
A protocol that makes laid out contents of the collection view
*/
public protocol CollectionViewLayoutType: ViewModifier {

}

public enum CollectionViewLayouts {

public struct PlatformList: CollectionViewLayoutType {

public func body(content: Content) -> some View {
SwiftUI.List {
content
}
}
}

public struct List<Separator: View>: CollectionViewLayoutType {

public let direction: CollectionViewListDirection
public let contentPadding: EdgeInsets

private let separator: Separator

public init(
direction: CollectionViewListDirection,
contentPadding: EdgeInsets = .init(),
@ViewBuilder separator: () -> Separator
) {
self.direction = direction
self.contentPadding = contentPadding
self.separator = separator()
}

public func body(content: Content) -> some View {
switch direction {
case .vertical:

ScrollView(.vertical) {
LazyVStack {
VariadicViewReader(readingContent: content) { children in
let last = children.last?.id
ForEach(children) { child in
child
if child.id != last {
separator
}
}
}
}
.padding(contentPadding)
}

case .horizontal:

ScrollView(.horizontal) {
LazyHStack {
VariadicViewReader(readingContent: content) { children in
let last = children.last?.id
ForEach(children) { child in
child
if child.id != last {
separator
}
}
}
}
.padding(contentPadding)
}

}
}

}

public struct Grid: CollectionViewLayoutType {

public func body(content: Content) -> some View {
// FIXME:
}
}

}

extension CollectionViewLayoutType where Self == CollectionViewLayouts.List<EmptyView> {

public static var list: Self {
CollectionViewLayouts.List(
direction: .vertical,
separator: { EmptyView() }
)
}

}

extension CollectionViewLayoutType {

public static func list<Separator: View>(
@ViewBuilder separator: () -> Separator
) -> Self where Self == CollectionViewLayouts.List<Separator> {
.init(direction: .vertical, separator: separator)
}

}


extension CollectionViewLayoutType where Self == CollectionViewLayouts.Grid {

public static var grid: Self {
CollectionViewLayouts.Grid()
}

}

public enum SelectAction {
case selected
case deselected
}

public protocol CollectionViewSelection<Item> {

associatedtype Item: Identifiable

/// Returns whether the item is selected or not
func isSelected(for id: Item.ID) -> Bool

/// Returns whether the item is enabled to be selected or not
func isEnabled(for id: Item.ID) -> Bool

/// Update the selection state
func update(isSelected: Bool, for item: Item)
}

extension CollectionViewSelection {

public static func single<Item: Identifiable>(
selected: Item.ID?,
onChange: @escaping (_ selected: Item?) -> Void
) -> Self where Self == CollectionViewSelectionModes.Single<Item> {
.init(
selected: selected,
onChange: onChange
)
}

public static func multiple<Item: Identifiable>(
selected: Set<Item.ID>,
canMoreSelect: Bool,
onChange: @escaping (_ selected: Item, _ selection: SelectAction) -> Void
) -> Self where Self == CollectionViewSelectionModes.Multiple<Item> {
.init(
selected: selected,
canMoreSelect: canMoreSelect,
onChange: onChange
)
}


}

public enum CollectionViewSelectionModes {

public struct None<Item: Identifiable>: CollectionViewSelection {

public init() {

}

public func isSelected(for id: Item.ID) -> Bool {
false
}

public func isEnabled(for id: Item.ID) -> Bool {
true
}

public func update(isSelected: Bool, for item: Item) {

}
}

public struct Single<Item: Identifiable>: CollectionViewSelection {

private let selected: Item.ID?
private let onChange: (_ selected: Item?) -> Void

public init(
selected: Item.ID?,
onChange: @escaping (_ selected: Item?) -> Void
) {
self.selected = selected
self.onChange = onChange
}

public func isSelected(for id: Item.ID) -> Bool {
self.selected == id
}

public func isEnabled(for id: Item.ID) -> Bool {
return true
}

public func update(isSelected: Bool, for item: Item) {
if isSelected {
onChange(item)
} else {
onChange(nil)
}
}

}

public struct Multiple<Item: Identifiable>: CollectionViewSelection {

private let selected: Set<Item.ID>
private let canMoreSelect: Bool
private let onChange: (_ selected: Item, _ action: SelectAction) -> Void

public init(
selected: Set<Item.ID>,
canMoreSelect: Bool,
onChange: @escaping (_ selected: Item, _ action: SelectAction) -> Void
) {
self.selected = selected
self.canMoreSelect = canMoreSelect
self.onChange = onChange
}

public func isSelected(for id: Item.ID) -> Bool {
self.selected.contains(id)
}

public func isEnabled(for id: Item.ID) -> Bool {
if isSelected(for: id) {
return true
}
return canMoreSelect
}

public func update(isSelected: Bool, for item: Item) {
if isSelected {
onChange(item, .selected)
} else {
onChange(item, .deselected)
}
}
}

}


/// Still searching better name
/// - built on top of SwiftUI only
@available(iOS 16, *)
Expand Down
121 changes: 121 additions & 0 deletions Sources/CollectionView/CollectionViewLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import SwiftUI

public enum CollectionViewListDirection {
case vertical
case horizontal
}

/**
A protocol that makes laid out contents of the collection view
*/
public protocol CollectionViewLayoutType: ViewModifier {

}

public enum CollectionViewLayouts {

public struct PlatformList: CollectionViewLayoutType {

public func body(content: Content) -> some View {
SwiftUI.List {
content
}
}
}

public struct List<Separator: View>: CollectionViewLayoutType {

public let direction: CollectionViewListDirection
public let contentPadding: EdgeInsets

private let separator: Separator

public init(
direction: CollectionViewListDirection,
contentPadding: EdgeInsets = .init(),
@ViewBuilder separator: () -> Separator
) {
self.direction = direction
self.contentPadding = contentPadding
self.separator = separator()
}

public func body(content: Content) -> some View {
switch direction {
case .vertical:

ScrollView(.vertical) {
LazyVStack {
VariadicViewReader(readingContent: content) { children in
let last = children.last?.id
ForEach(children) { child in
child
if child.id != last {
separator
}
}
}
}
.padding(contentPadding)
}

case .horizontal:

ScrollView(.horizontal) {
LazyHStack {
VariadicViewReader(readingContent: content) { children in
let last = children.last?.id
ForEach(children) { child in
child
if child.id != last {
separator
}
}
}
}
.padding(contentPadding)
}

}
}

}

public struct Grid: CollectionViewLayoutType {

public func body(content: Content) -> some View {
// FIXME:
}
}

}

extension CollectionViewLayoutType where Self == CollectionViewLayouts.List<EmptyView> {

public static var list: Self {
CollectionViewLayouts.List(
direction: .vertical,
separator: { EmptyView() }
)
}

}

extension CollectionViewLayoutType {

public static func list<Separator: View>(
@ViewBuilder separator: () -> Separator
) -> Self where Self == CollectionViewLayouts.List<Separator> {
.init(direction: .vertical, separator: separator)
}

}


extension CollectionViewLayoutType where Self == CollectionViewLayouts.Grid {

public static var grid: Self {
CollectionViewLayouts.Grid()
}

}
Loading