@latticejs/infinite-list
v1.0.1-beta.2
Published
A set of material-ui components adapted to support infinite list feature.
Downloads
15
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 theparent width
Set a fixed width for the list. If it is undefined it will use AutoSizer
to detect the width.
height
number
| defaults to theparent 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
andvalue
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.