Skip to content

Instantly share code, notes, and snippets.

@jefo
Created April 3, 2019 15:49
Show Gist options
  • Save jefo/fbe05ad6092cbebd37d2c2c141f289e3 to your computer and use it in GitHub Desktop.
Save jefo/fbe05ad6092cbebd37d2c2c141f289e3 to your computer and use it in GitHub Desktop.
import * as React from 'react';
import cn from 'classnames';
import { TransitionGroup } from 'react-transition-group';
import { Preloader } from '@shared/Preloader';
import { Scrollable } from '@shared/Scrollable';
import { NextPageButton } from '@shared/NextPageButton';
import { FilterForm } from '../FilterForm';
import { IInputProps } from '@src/shared/inputs/Input/Input';
import Scrollbars from 'react-custom-scrollbars';
import Row from './Row';
import { IPaginatedParams } from '@src/core/interfaces';
import { AppearItem } from '@src/shared/AnimateAppear';
const styles = require('./styles.scss');
export interface IGridProps<T = any> {
items?: T[];
filterFields?: IInputProps[];
totalCount: number;
isFetching: boolean;
offset: number;
limit: number;
rounded?: boolean;
load: (params: IPaginatedParams, replace?: boolean) => void;
}
export interface ICommonGridRow {
pk: number;
icon: string;
title: string;
parentName: string;
[id:string]: any;
}
export interface IFilter {
[id: string]: string;
}
export interface IContactsListState {
filter: IFilter;
}
/**
* Controlled filters HoC
*/
export default class Grid extends React.Component<
IGridProps,
IContactsListState
> {
static Row = Row;
state = {
filter: {},
};
scrollable: Scrollable;
handleFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
const { limit, load } = this.props;
let { filter } = this.state;
filter = { ...filter, [name]: value };
this.setState({ filter });
load({
...filter,
limit,
offset: 0,
}, true);
};
handleOnScroll = () => {
if (
this.scrollable &&
this.props.offset > 0 &&
!this.props.isFetching
) {
const {
totalCount,
offset,
limit,
load,
items,
} = this.props;
const scrollbars: Scrollbars = (this.scrollable as any).refs
.scrollbars;
if (totalCount > items.length) {
const {
clientHeight,
scrollHeight,
scrollTop,
} = scrollbars.getValues();
if (clientHeight + scrollTop > scrollHeight - 140) {
load({
...this.state.filter,
limit,
offset: offset + limit,
});
}
}
}
};
handleLoadMoreClick = () => {
const { limit, offset } = this.props;
this.props.load({
...this.state.filter,
limit,
offset: offset + limit,
});
};
render() {
const {
filterFields,
items,
totalCount,
isFetching,
offset,
limit,
rounded,
} = this.props;
const { filter } = this.state;
const children = React.Children.map(
this.props.children,
(child, i) => <AppearItem
index={i}
className={styles.itemAppear}
>
{child}
</AppearItem>,
);
return (
<div className={cn(styles.grid, { [styles.grid_br]: rounded })}>
{filterFields && (
<FilterForm className={cn({ [styles.rounded]: rounded })}>
{filterFields.map((field) => (
<FilterForm.Field
{...field}
value={filter[field.name] || ''}
onChange={this.handleFilterChange}
/>
))}
</FilterForm>
)}
<Scrollable
ref={(node) => { this.scrollable = node; }}
autoHide
onScroll={this.handleOnScroll}
style={{
flex: '1 1 0',
height: 'calc(100% - 60px)',
}}
>
{items.length > 0 ? (
<TransitionGroup className={styles.userList} component="ul">
{children}
</TransitionGroup>
) : (
<div className={styles.preload}>
{isFetching ? <Preloader /> : <p>Список пуст</p>}
</div>
)}
{items.length > 0 && offset < limit && (
<div className={styles.nextPage}>
<NextPageButton
loaded={items.length}
total={totalCount}
busy={isFetching}
onClick={this.handleLoadMoreClick}
/>
</div>
)}
</Scrollable>
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment