@latticejs/infinite-list

A set of material-ui components adapted to support infinite list feature.

Stats

stars 🌟issues ⚠️updated 🛠created 🐣size 🏋️‍♀️
@latticejs/infinite-list
Minified + gzip package size for @latticejs/infinite-list in KB

Readme

@latticejs/infinite-list

A set of material-ui components adapted to support infinite list feature.

Table of contents

Install

npm install @latticejs/infinite-list

ScrollLoader

The ScrollLoader is a react component based on set of components from react-virtualized: InfiniteLoader, AutoSizer and List.

It's designed to keep simple the creation of a infinite loader list and at the same time you can access to each feature of react-virtualized if you want.

List and TableBody works on top of this component.

ScrollLoader doesn't use the native scroll, it uses react-custom-scrollbars.

Usage

import React, { Component } from 'react';
import { ScrollLoader } from '@latticejs/infinite-list';

class App extends Component {
  state = {
    // loaded items
    list: [{ text: 'item 1' }, { text: 'item 2' }],
    // total items (loaded & missing items)
    rowCount: 100
  };

  loadMore = async ({ startIndex, stopIndex }) => {
    // load missing items (returns a Promise)
  };

  render() {
    const { list, rowCount } = this.state;

    return (
      <div>
        <ScrollLoader list={list} loadMore={this.loadMore} rowCount={rowCount} rowHeight={40}>
          {({ item, isEmpty, key, style }) => {
            if (isEmpty) {
              return <h4>Empty list</h4>;
            }

            if (!item) {
              // is loading
              return (
                <div key={key} style={style}>
                  loading
                </div>
              );
            }

            // loaded
            return (
              <div key={key} style={style}>
                {item.text}
              </div>
            );
          }}
        </ScrollLoader>
      </div>
    );
  }
}

API

children

function(props: { item, isEmpty, key, style }) | required

Children function prop to define how to render each item of the list.

  • item: Current item of the iteration, it can be null is the list is empty or is loading.
  • isEmpty: Boolean that defines if the list is empty.
  • key: Key of the current item.
  • style: Style props required to apply in each item. It's really important to use it, otherwise the scroll it's never going to work

list

Array | required

List of items already loaded.

rowCount

number | required

Total count of items. This is necessary to create the entire list of items.

You can think about it like the sql

select count(*) from table

it doesn't matter if you loaded only the first 10 items.

loadMore

function(props: { startIndex, stopIndex }) => Promise | required

This is a required callback to be invoked when more rows must be loaded because ScrollLoader can't find some item in the list.

The returned Promise should be resolved once row data has finished loading. It will be used to determine when to refresh the list with the newly-loaded data. This callback may be called multiple times in reaction to a single scroll event.

rowHeight

(number | function) | required

Either a fixed row height (number) or a function that returns the height of a row given its index: ({ index: number }) => number

findItem

function({ index: number }) => (undefined | item) | defaults to: ({ index }) => list[index]

Function to define how ScrollLoader is going to search each item on the list.

isRowLoaded

function({ index: number }) => Boolean | defaults to: ({ index }) => !!findItem({ index })

Function responsible for tracking the loaded state of each row. By default it uses the findItem function.

width

number | defaults to the parent width

Set a fixed width for the list. If it is undefined it will use AutoSizer to detect the width.

height

number | defaults to the parent height

Set a fixed height for the list. If it is undefined it will use AutoSizer to detect the height.

rvInfiniteLoaderProps

object | defaults to {}

Set options to the react-virtualized InfiniteLoader instance.

rvAutoSizerProps

object | defaults to {}

Set options to the react-virtualized AutoSizer instance.

rvListProps

object | defaults to {}

Set options to the react-virtualized List instance.

rvScrollProps

object | defaults to {}

Set options to the react-custom-scrollbars instance.

List

The List component it's just a wrapper around the Material-UI List and our ScrollLoader.

So it has the same API of Material-UI List and the ScrollLoader.

NOTE: react-virtualized needs that each component were div tags. So, we provide a wrapper for Material-UI ListItem and ListItemSecondaryAction components too.

Usage

import React, { Component } from 'react';
import { List, ListItem } from '@latticejs/infinite-list';

import ListItemText from '@material-ui/core/ListItemText';
import Paper from '@material-ui/core/Paper';

class App extends Component {
  ...
  render() {
    const { list, rowCount } = this.state;

    return (
      <Paper>
        <List list={list} loadMore={this.loadMore} rowCount={rowCount} rowHeight={40}>
          {({ item, isEmpty, key, style }) => {
            if (isEmpty) {
              return <h4>Empty list</h4>;
            }

            if (!item) {
              // is loading
              return (
                <ListItem key={key} style={style}>
                  <ListItemText primary="loading..." />
                </ListItem>
              );
            }

            // is loaded
            return (
              <ListItem key={key} style={style}>
                <ListItemText primary={item.text} />
              </ListItem>
            );
          }}
        </List>
      </Paper>
    );
  }
}

Table

Lattice's Table component provides support for displaying tabulated data with infinite data loading support.

Since react-virtualized needs that each component were div tags we need to create wrappers and apply some custom styles for the next Material-UI components:

TableBody

The TableBody is a special component created on top of the ScrollLoader, the idea is that in general the tbody represents a tag where you render inside a list, in this case an infinite loader list.

You can use the entire API of the ScrollLoader.

Usage

import React, { Component } from 'react';
import {
  Table,
  TableBody,
  TableHead,
  TableRow,
  TableCell
} from '@latticejs/infinite-list';

import Paper from '@material-ui/core/Paper';

class App extends Component {
  ...
  render() {
    const { list, rowCount } = this.state;

    return (
      <Paper>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>Index</TableCell>
              <TableCell>Title</TableCell>
              <TableCell>Timestamp</TableCell>
            </TableRow>
          </TableHead>
          <TableBody
            list={list}
            loadMore={this.loadMore}
            rowCount={rowCount}
            rowHeight={48}
            height={200}
          >
            {({ item, isEmpty, key, style }) => {
              if (isEmpty) {
                return <h4>Empty list</h4>;
              }

              if (!item) {
                return (
                  <TableRow key={key} style={style}>
                    <TableCell>loading...</TableCell>
                  </TableRow>
                );
              }

              return (
                <TableRow key={key} style={style}>
                  <TableCell>{item.index}</TableCell>
                  <TableCell>{item.title}</TableCell>
                  <TableCell>{item.timestamp}</TableCell>
                </TableRow>
              );
            }}
          </TableBody>
        </Table>
      </Paper>
    );
  }
}

TableOrderCell

Component created on top of TableCell, TableSortLabel and Tooltip to provide an easy way of create a table order cell.

It supports multiple orders using shift + click out of the box.

You can use Material-UI TableCell API.

Usage

import React, { Component } from 'react';
import {
  Table,
  TableHead,
  TableRow,
  TableCell,
  TableOrderCell
} from '@latticejs/infinite-list';

import Paper from '@material-ui/core/Paper';

class App extends Component {
  state = {
    orderBy: []
  };

  handleOrder = orderBy => {
    this.setState({ orderBy });
  };

  render() {
    const { orderBy } = this.state;

    return (
      <Paper>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>Index</TableCell>
              <TableOrderCell
                field="title"
                title="Title"
                orderBy={orderBy}
                handleOrder={this.handleOrder} />
              <TableOrderCell
                field="timestamp"
                title="Timestamp"
                orderBy={orderBy}
                handleOrder={this.handleOrder} />
            </TableRow>
          </TableHead>
        </Table>
      </Paper>
    );
  }
}

API

field

string | required

The field prop is required to create an Array of orderBy.

orderBy

Array<Order> | required

Order: { field: string, direction: ('asc'|'desc') }

The orderBy is the current Order object list of your table state.

handleOrder

function(orderBy: []) | required

Callback function called when there is a new order.

multiSort

boolean | defaults to: true

Used to indicate if multiple orders are allowed.

title

string | defaults to: ''

The title will be used by the Tooltip component.

TableSearchCell

Component on top of TableCell to provide an easy way to create a table search cell.

It supports input debounce out of the box.

Since extends from the Material-UI TableCell you can use their API.

Usage

import React, { Component } from 'react';
import {
  Table,
  TableHead,
  TableRow,
  TableCell,
  TableSearchCell
} from '@latticejs/infinite-list';

import Paper from '@material-ui/core/Paper';
import Input from '@material-ui/core/Input';

class App extends Component {
  state = {
    filterBy: []
  };

  handleSearch = filterBy => {
    this.setState({ filterBy });
  };

  render() {
    const { orderBy } = this.state;

    return (
      <Paper>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>Index</TableCell>
              <TableCell>Title</TableCell>
              <TableCell>Timestamp</TableCell>
            </TableRow>
            <TableRow>
              <TableCell />
              <TableSearchCell field="name" debounce={200} filterBy={filterBy} handleSearch={this.handleSearch}>
                {({ inputProps }) => <Input fullWidth {...inputProps} />}
              </TableSearchCell>
              <TableSearchCell field="email" filterBy={filterBy} handleSearch={this.handleSearch}>
                {({ inputProps }) => <Input fullWidth {...inputProps} />}
              </TableSearchCell>
            </TableRow>
          </TableHead>
        </Table>
      </Paper>
    );
  }
}

API

field

string | required

The field prop is required to create an Array of filterBy.

filterBy

Array<Filter> | required

Filter: { field: string, value: string }

The filterBy is the current Filter object list of your table state.

handleSearch

function(filterBy: []) | required

Callback function called when there is a new filter.

children

function(props: SearchProps) | required

SearchProps: { inputProps: { name: string, value: string, onChange: function }, updateValue: function }

  • inputProps: Set of props for an input component that has an onChange and value prop.
  • updateValue: Function to use in case of input component doesn't have onChange support.

debounce

number | defaults to: 300

Set the time that the TableSearchCell has to wait during an input onchange event before to run the handleSearch callback.

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.