A container for displaying large ordered collections of data items and presenting them using arbitrary layouts (e.g. lists, grids). Large collections can be scrolled smoothly by displaying only a limited number of elements. This is also known as "windowing" or "virtualization". Changes to the data (remove, add, move) and changes to the layout are properly animated by the collection view. The library is inspired by iOS' UICollectionView and Android's RecyclerView.
The collection view gets its data from a delegate and gets its visual information from a layout. Currently there is a grid layout, which presents the collection in rows and columns, and a simple list layout, which presents the collection in rows. Custom layouts can be implemented easily. Contributions are welcome!
The collection view also handles resizing of the container properly, maintaining the current position in the collection:
Elements can be individually animated:
import { CollectionView, GridLayout } from 'collection-view'
class Delegate {
constructor(items) {
this.items = items
}
getCount() {
return this.items.length
}
configureElement(element, index) {
element.textContent = this.items[index]
}
}
let items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10"]
let delegate = new Delegate(items)
let layout = new GridLayout()
let contentElement = document.getElementById('content')
let view = new CollectionView(contentElement, layout, delegate)
Implement a delegate for your collection. Instantiate a layout, like a grid or list, and configure it. Finally, instantiate a collection view, providing the DOM element which should contain the elements representing the items in the collection, the layout, and the delegate.
If the grid or list layouts do not fit your needs you can also implement a custom layout.
The delegate object is responsible for defining how many items the collection view should display and configuring the elements corresponding to the items.
getCount(): number (required)
Return the number of items in the collection.
Similar to UICollectionViewDataSource.collectionView(_:numberOfItemsInSection:)
configureElement(element: HTMLElement, index: number) (required)
Configure the DOM element that corresponds to the item in the collection at the given index. The element might have previously been used to represent another item in the collection, so ensure to properly restore it to its initial state. If this method registers events, implement invalidateElement
(see below) and make sure to unregister them there.
Similar to UICollectionViewDataSource.collectionView(_:cellForItemAtIndexPath:)
invalidateElement(element: HTMLElement, index: number) (optional)
Called when an element no longer displays the item at the given index. Implement this method to e.g. unregister events.
onScroll(view: CollectionView) (optional)
Called when the collection view is scrolled.
Similar to UIScrollViewDelegate.scrollViewDidScroll(_:)
getStyle(index: number, phase: CollectionViewAnimationPhase, info, position: Position): Style (optional)
Called to determine the style of the element at the given index for the given animation phase and position.
The returned style is an object with hyphen-style CSS properties and string values, e.g. {"background-color": "red"}
.
See below for the info
parameter.
phase: CollectionViewAnimationPhase
ELEMENT_APPEARING
: The element is about to appear ("is entering")
ELEMENT_APPEARED
: The element has appeared
ELEMENT_DISAPPEARED
: The element has disappeared ("has left")
getAnimation(index: number, info, property: string, reason: CollectionViewAnimationReason): Animation (optional)
Return the animation for the given property of the element at the given index.
Animation
duration: number (optional): The duration of the CSS transition
delay: number (optional): The delay of the CSS transition
timingFunction: string (optional): The timing function of the CSS transition
See below for the info
parameter.
Indicates why the element is animated.
ELEMENT_ADDITION
: The element is being addedELEMENT_REMOVAL
: The element is being removedELEMENT_MOVE
: The element is being repositionedLAYOUT_UPDATE
: The layout is updated. This is also the case when the collection view is being resized.The getStyle
and getAnimation
delegate methods are also passed an info
parameter, which is layout information for the element, provided by the current layout.
When a list layout is used, it is an object with a row
property, and when a grid layout layout is used, it is an object containing row
and column
properties.
The collection view supports transitions between different collections. As it is not aware of the underlying data itself, the changes have to be provided to the collection view explicitly, in the form of the indices of the items which were removed, added, and moved, passed to the method:
changeIndices(removed: number[], added: number[], moved: Map.<number, number>): Promise
removed: number[]
Indices which were removed, referring to the collection before the changes
added: number[]
Indices which were added, referring to the collection after the changes
moved: Map.<number, number>
Indices which were moved. The keys are indices referring to the collection before the changes, and the values are indices referring to the collection after the changes.
May also be passed as an Object.<string, number>
For example, when transitioning from the original collection [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] to the target collection [1, 15, 16, 3, 6, 8, 4, 10, 11]:
The items at indices [1, 4, 6, 8] in the original collection are not in the target collection anymore (removed):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
The items at indices [1, 2] in the target collection are new in the target collection (added):
[1, 15, 16, 3, 6, 8, 4, 10, 11]
The item at index 3 in the original collection is now at index 6 in the target collection (moved):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
[1, 15, 16, 3, 6, 8, 4, 10, 11]
Therefore, the method call would be changeIndices([1, 4, 6, 8], [1, 2], new Map([[3, 6]]))
(or alternatively changeIndices([1, 4, 6, 8], [1, 2], new Map([3, 6]))
for older environments).
Similar to calling UICollectionView.deleteItems(at:)
, UICollectionView.insertItems(at:)
, and multiple UICollectionView.moveItem(at:to:)
as part of a call to performBatchUpdates(_:completion:)
The GridLayout
displays collection items in a grid. All items are the same size and flow from
one row or column (depending on the direction) to the next.
It is similar to UICollectionViewFlowLayout
.
direction: Direction
The direction in which the collection view scrolls.
If Direction.VERTICAL
, the collection view scrolls vertically and items flow in rows from left to right.
If Direction.HORIZONTAL
, the collection view scrolls horizontally and items flow in columns from top to bottom.
Default: Direction.VERTICAL
Similar to UICollectionViewFlowLayout.scrollDirection
itemSize: Size
The size (width and height) of each item.
Default: new Size(200, 200)
Similar to UICollectionViewFlowLayout.itemSize
spacing: Spacing
The spacing (horizontal and vertical) between the items.
Default: new Spacing(20, 20)
Similar to UICollectionViewFlowLayout.minimumInteritemSpacing
and UICollectionViewFlowLayout.minimumLineSpacing
insets: Insets
The insets (padding) of the whole content: top, bottom, left, right.
Default: new Insets(10, 10, 10, 10)
Similar to UICollectionViewFlowLayout.sectionInset
The ListLayout
displays collection items in a list. All items have the same height.
rowHeight: number
The height of each row.
Default: 200
The collection view supports transitions between different layouts. Simply instantiate a new layout, configure it, and call:
updateLayout(newLayout: CollectionViewLayout): Promise
For example, this allows changing the item size or direction of a grid layout. The collection view properly maintains the position in the collection.
The layout object is responsible for defining the size of the collection view content element and positions of the elements representing the items of the collection. Normally it is not necessary to implement a custom layout, as the grid and list layout should be sufficient in most use-cases. Still, if another layout is desired, the following methods need to be implemented:
getContentSize(count: number, containerSize: Size): Size (required)
Return the width and height of the whole content, i.e. the space that all elements occupy, not just the content that is currently visible.
updateContainerSize(containerSize: Size) (optional)
Informs the layout about the new size of the container. Might be used to recalculate positioning information.
getIndices(ranges: Ranges, count: number, containerSize: Size): number[] (required)
Return an array of the visible indices in the region (given by ranges). The given container size might be different from the container size provided to updateContainerSize
if the collection view is updated to another layout.
Similar to UICollectionViewLayout.layoutAttributesForElements(in:)
getElementPosition(index: number): Position (required)
Return the top-left position of the element representing the item at the specified index in the collection.
Similar to UICollectionViewLayout.layoutAttributesForItem(at:)
configureElement(element: HTMLElement, index: number) (required)
Apply visual details to the element, e.g. set the size.
convertPositionInSize(position: Position, newContainerSize: Size, oldLayout: CollectionViewLayout): Position (required)
Calculate the new scroll position for the given current scroll position in the given layout. The old layout may be of another type. Used to maintain the current scroll position in the collection view when it is transitioning to another layout and when the collection view is resizing.
contentSize: Size
The size of the scrolled content. Computed by the layout based on the data returned from the delegate.
Similar to UIScrollView.contentSize
containerSize: Size
The size of the container wrapping the scrolled content, i.e. the size of the content that is actually displayed, a region of contentSize
.
scrollPosition: Position
The point at which the origin of the content is offset from the origin of the collection view.
Similar to UIScrollView.contentOffset
thresholds: object
Specifies the additional values by which the region is extended that is used to determine the visible elements. Increasing the values leads to more elements staying attached to the scroll element. This improves appearance, as elements are already properly positioned and configured when they are becoming visible to the user. However, it is also likely to reduce scroll performance, as more elements need to be rendered by the browser.
Keys: left
, top
, right
, and bottom
.
Default: all values are CollectionView.DEFAULT_THRESHOLD
= 3000
animationDuration: number
Specifies how long animations take by default. Animations durations for each element can also be provided through the delegate method getAnimation
.
Default: CollectionView.DEFAULT_ANIMATION_DURATION
= 400
The examples
directory contains several examples demonstrating the features of the collection view. To try them out, go to each subdirectory, run webpack
and open the index.html
.
For usage with React, see examples/react
.