src/components/widgets/List/ListContainer.jsx
import React from 'react';
import { connect } from 'react-redux';
import cn from 'classnames';
import PropTypes from 'prop-types';
import { compose } from 'recompose';
import { makeWidgetPageSelector } from '../../../selectors/widgets';
import {
map,
forOwn,
isEmpty,
isEqual,
debounce,
keys,
get,
find,
} from 'lodash';
import widgetContainer from '../WidgetContainer';
import List from './List';
import withColumn from '../Table/withColumn';
import TableCell from '../Table/TableCell';
import {
withContainerLiveCycle,
withWidgetHandlers,
} from '../Table/TableContainer';
import { createStructuredSelector } from 'reselect';
import { setTableSelectedId } from '../../../actions/widgets';
const ReduxCell = withColumn(TableCell);
/**
* Контейнер виджета ListWidget
* @reactProps {string} widgetId - id виджета
* @reactProps {object} toolbar - конфиг тулбара
* @reactProps {boolean} disabled - флаг активности
* @reactProps {object} actions - объект экшенов
* @reactProps {string} pageId - id страницы
* @reactProps {object} paging - конфиг пагинации
* @reactProps {string} className - класс
* @reactProps {object} style - объект стилей
* @reactProps {object} filter - конфиг фильтра
* @reactProps {object} dataProvider - конфиг dataProvider
* @reactProps {boolean} fetchOnInit - флаг запроса при инициализации
* @reactProps {object} list - объект конфиг секций в виджете
* @reactProps {object|null} rowClick - кастомный клик
* @reactProps {boolean} hasMoreButton - флаг включения загрузки по нажатию на кнопку
* @reactProps {number} maxHeight - максимальная высота виджета
* @reactProps {boolean} fetchOnScroll - запрос при скролле
* @reactProps {boolean} hasSelect - флаг выбора строк
*/
class ListContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
needToCombine: false,
datasource: props.datasource,
};
this.getWidgetProps = this.getWidgetProps.bind(this);
this.mapSectionComponents = this.mapSectionComponents.bind(this);
this.renderCell = this.renderCell.bind(this);
this.handleItemClick = this.handleItemClick.bind(this);
this.handleFetchMore = this.handleFetchMore.bind(this);
}
componentDidUpdate(prevProps) {
const { datasource: prevDatasource } = prevProps;
const { datasource: currentDatasource, onResolve, selectedId } = this.props;
const { needToCombine } = this.state;
if (currentDatasource && !isEqual(prevDatasource, currentDatasource)) {
let newDatasource = [];
if (needToCombine) {
newDatasource = [...this.state.datasource, ...currentDatasource];
} else {
newDatasource = currentDatasource.slice();
}
this.setState(
{
needToCombine: false,
datasource: newDatasource,
},
() => {
const model = selectedId
? find(currentDatasource, item => item.id === selectedId)
: currentDatasource[0];
if (model) onResolve(model);
}
);
}
}
renderCell(section) {
if (!section) return;
return (
<ReduxCell
{...section}
widgetId={this.props.widgetId}
positionFixed={false}
modifiers={{}}
className={cn('n2o-widget-list-cell', get(section, 'className', ''))}
/>
);
}
handleItemClick(index) {
const { onResolve, datasource, rowClick, onRowClickAction } = this.props;
onResolve(datasource[index]);
if (rowClick) {
onRowClickAction();
}
}
handleFetchMore() {
const { page, datasource, onFetch } = this.props;
if (!isEmpty(datasource)) {
this.setState({ needToCombine: true }, () => onFetch({ page: page + 1 }));
}
}
mapSectionComponents() {
const { list } = this.props;
const { datasource } = this.state;
return map(datasource, item => {
let mappedSection = {};
forOwn(list, (v, k) => {
mappedSection[k] = this.renderCell({
...list[k],
id: v.id,
model: item,
});
});
return mappedSection;
});
}
getWidgetProps() {
const {
hasMoreButton,
rowClick,
placeholder,
maxHeight,
fetchOnScroll,
divider,
hasSelect,
selectedId,
} = this.props;
return {
onFetchMore: this.handleFetchMore,
onItemClick: this.handleItemClick,
data: this.mapSectionComponents(),
rowClick,
hasMoreButton,
placeholder,
maxHeight,
fetchOnScroll,
divider,
hasSelect,
selectedId,
};
}
render() {
return <List {...this.getWidgetProps()} />;
}
}
ListContainer.propTypes = {
widgetId: PropTypes.string,
toolbar: PropTypes.object,
disabled: PropTypes.bool,
actions: PropTypes.object,
pageId: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
filter: PropTypes.object,
dataProvider: PropTypes.object,
fetchOnInit: PropTypes.bool,
list: PropTypes.object,
fetchOnScroll: PropTypes.bool,
rowClick: PropTypes.func,
hasMoreButton: PropTypes.bool,
maxHeight: PropTypes.number,
datasource: PropTypes.array,
hasSelect: PropTypes.bool,
};
ListContainer.defaultProps = {
datasource: [],
rowClick: null,
hasMoreButton: false,
toolbar: {},
disabled: false,
actions: {},
className: '',
style: {},
filter: {},
list: {},
fetchOnScroll: false,
hasSelect: false,
};
const mapStateToProps = createStructuredSelector({
page: (state, props) => makeWidgetPageSelector(props.widgetId)(state),
});
export default compose(
widgetContainer(
{
mapProps: props => {
return {
...props,
onResolve: debounce(newModel => {
props.onResolve(newModel);
if (props.selectedId != newModel.id) {
props.dispatch(setTableSelectedId(props.widgetId, newModel.id));
}
}, 100),
};
},
},
'ListWidget'
),
withContainerLiveCycle,
withWidgetHandlers,
connect(mapStateToProps)
)(ListContainer);