Skip to content

Latest commit

 

History

History
306 lines (159 loc) · 14.5 KB

README.md

File metadata and controls

306 lines (159 loc) · 14.5 KB

collection-view

Build Status

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!

Scrolling

The collection view also handles resizing of the container properly, maintaining the current position in the collection:

Resizing

Elements can be individually animated:

Staggering

Pivot

Usage

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.

Delegate

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.

    • reason: CollectionViewAnimationReason

      Indicates why the element is animated.

      • ELEMENT_ADDITION: The element is being added
      • ELEMENT_REMOVAL: The element is being removed
      • ELEMENT_MOVE: The element is being repositioned
      • LAYOUT_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.

Changing the data

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:)

Grid layout

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.

List layout

The ListLayout displays collection items in a list. All items have the same height.

  • rowHeight: number

    The height of each row.

    Default: 200

Changing the layout

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.

Custom layout

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.

    Similar to UICollectionViewLayout.collectionViewContentSize

  • 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.

Collection view

  • 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

Examples

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.

React

For usage with React, see examples/react.