Home Reference Source

src/components/widgets/AdvancedTable/AdvancedTableContainer.jsx

import React from 'react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import {
  map,
  isEqual,
  find,
  isEmpty,
  debounce,
  pick,
  forOwn,
  is,
  omit,
} from 'lodash';
import AdvancedTable from './AdvancedTable';
import widgetContainer from '../WidgetContainer';
import { setTableSelectedId } from '../../../actions/widgets';
import { TABLE } from '../widgetTypes';
import columnHOC from '../Table/withColumn';
import TableCell from '../Table/TableCell';
import { setModel } from '../../../actions/models';
import { PREFIXES } from '../../../constants/models';
import PropTypes from 'prop-types';
import { withWidgetHandlers } from '../Table/TableContainer';
import { makeGetFilterModelSelector } from '../../../selectors/models';

const isEqualCollectionItemsById = (data1 = [], data2 = [], selectedId) => {
  const predicate = ({ id }) => id == selectedId;
  return isEqual(find(data1, predicate), find(data2, predicate));
};

const ReduxCell = columnHOC(TableCell);

class AdvancedTableContainer extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      data: this.mapData(props.datasource),
      columns: this.mapColumns(),
    };

    this._filter = props.filters;

    this.getTableProps = this.getTableProps.bind(this);
    this.mapColumns = this.mapColumns.bind(this);
    this.mapData = this.mapData.bind(this);
    this.renderCell = this.renderCell.bind(this);
    this.handleSetFilter = this.handleSetFilter.bind(this);
    this.onEdit = this.onEdit.bind(this);
  }

  componentDidUpdate(prevProps, prevState) {
    const {
      selectedId: prevSelectedId,
      datasource: prevDatasource,
      onResolve,
    } = prevProps;
    const { hasSelect, datasource, selectedId } = this.props;

    if (!isEqual(prevProps.datasource, this.props.datasource)) {
      this.setState({
        data: this.mapData(this.props.datasource),
        columns: this.mapColumns(),
      });
    }

    if (
      hasSelect &&
      !isEmpty(datasource) &&
      !isEqual(prevDatasource, datasource) &&
      (!selectedId ||
        !isEqual(prevSelectedId, selectedId) ||
        !isEqualCollectionItemsById(prevDatasource, datasource, selectedId))
    ) {
      const selectedModel = find(datasource, model => model.id == selectedId);
      const resolveModel = selectedModel || datasource[0];
      onResolve(resolveModel);
    }
  }

  componentDidMount(prevProps) {
    if (this.props.datasource) {
      this.setState({
        data: this.mapData(this.props.datasource),
        columns: this.mapColumns(),
      });
    }
  }

  renderCell(props) {
    const propStyles = pick(props, ['width']);
    return <ReduxCell {...propStyles} {...props} />;
  }

  handleSetFilter(filter) {
    const { onSetFilter, onFetch } = this.props;
    this._filter = {
      ...this._filter,
      [filter.id]: filter.value,
    };
    forOwn(this._filter, (v, k) => {
      if (!v || isEmpty(v)) delete this._filter[k];
    });
    onSetFilter({ ...this._filter });
    onFetch();
  }

  onEdit(value, index, id) {
    //TODO something
  }

  mapColumns() {
    const { cells, headers, widgetId, sorting, onSort } = this.props;
    return headers.map(header => {
      const cell = find(cells, c => c.id === header.id);
      return {
        ...header,
        title: this.renderCell({
          ...header,
          key: header.id,
          columnId: header.id,
          widgetId,
          as: 'div',
          sorting: sorting && sorting[header.id],
          onSort,
        }),
        label: header.title,
        dataIndex: header.id,
        columnId: header.id,
        key: header.id,
        hasSpan: cell.hasSpan,
        render: (value, record, index) => ({
          children: this.renderCell({
            index,
            key: cell.id,
            widgetId,
            columnId: cell.id,
            model: record,
            as: 'div',
            ...cell,
          }),
        }),
      };
    });
  }

  mapData(datasource) {
    if (!datasource) return;
    return datasource.map(item => {
      return {
        ...item,
        key: item.id,
      };
    });
  }

  getTableProps() {
    const props = omit(this.props, [
      'actions',
      'cells',
      'headers',
      'datasource',
      'dispatch',
      'onActionImpl',
      'onEdit',
      'onFetch',
      'pageId',
      'redux',
      'sorting',
      'widgetId',
    ]);

    return {
      ...props,
      onEdit: this.onEdit,
      columns: this.state.columns,
      data: this.state.data,
      onFilter: this.handleSetFilter,
    };
  }

  render() {
    return <AdvancedTable {...this.getTableProps()} />;
  }
}

AdvancedTableContainer.contextTypes = {
  resolveProps: PropTypes.func,
  expandedFieldId: PropTypes.string,
};

AdvancedTableContainer.defaultProps = {
  filters: {},
};

const mapStateToProps = (state, props) => {
  return {
    filters: makeGetFilterModelSelector(props.widgetId)(state, props),
  };
};

export default compose(
  widgetContainer(
    {
      mapProps: props => {
        return {
          widgetId: props.widgetId,
          pageId: props.pageId,
          headers: props.headers,
          cells: props.cells,
          isAnyTableFocused: props.isAnyTableFocused,
          isActive: props.isActive,
          hasFocus: props.hasFocus,
          hasSelect: props.hasSelect,
          autoFocus: props.autoFocus,
          datasource: props.datasource,
          selectedId: props.selectedId,
          sorting: props.sorting,
          rowClass: props.rowClass,
          onFetch: props.onFetch,
          onSort: props.onSort,
          onResolve: newModel => {
            props.onResolve(newModel);
            if (props.selectedId != newModel.id) {
              props.dispatch(setTableSelectedId(props.widgetId, newModel.id));
            }
          },
          onSetSelection: model => {
            props.dispatch(setModel(PREFIXES.multi, props.widgetId, model));
          },
          onSetFilter: filters => {
            props.dispatch(setModel(PREFIXES.filter, props.widgetId, filters));
          },
          onFocus: props.onFocus,
          size: props.size,
          actions: props.actions,
          redux: true,
          rowSelection: props.rowSelection,
          tableSize: props.tableSize,
          placeholder: props.placeholder,
          useFixedHeader: props.useFixedHeader,
          expandable: props.expandable,
          scroll: props.scroll,
          multiHeader: props.multiHeader,
          bordered: props.bordered,
          rowClick: props.rowClick,
          onActionImpl: props.onActionImpl,
          expandedFieldId: props.expandedFieldId,
        };
      },
    },
    TABLE
  ),
  withWidgetHandlers,
  connect(
    mapStateToProps,
    null
  )
)(AdvancedTableContainer);