"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _react = _interopRequireWildcard(require("react"));

var _propTypes = _interopRequireDefault(require("prop-types"));

var _immutable = _interopRequireDefault(require("immutable"));

var _reactIntl = require("react-intl");

var _reactRouterDom = require("react-router-dom");

var _get = _interopRequireDefault(require("lodash/get"));

var _cspaceLayout = require("cspace-layout");

var _dimensions = _interopRequireDefault(require("../../../styles/dimensions.css"));

var _SearchResultTable = _interopRequireDefault(require("../../../styles/cspace-ui/SearchResultTable.css"));

var _SearchResultEmpty = _interopRequireDefault(require("../../../styles/cspace-ui/SearchResultEmpty.css"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

const rowHeight = parseInt(_dimensions.default.inputHeight, 10);
const messages = (0, _reactIntl.defineMessages)({
  searchPending: {
    "id": "searchResultTable.searchPending",
    "defaultMessage": "\u22EF"
  }
});
/**
 * Determines if a column is sortable for a given search. A column is sortable if sortBy is truthy,
 * and the search is not constrained by a related record, or if it is, the field to sort by is not
 * complex. This is here to deal with CSPACE-5366 (searches with related record constraints are
 * done using CMIS, which can't see into complex fields). If that bug is ever fixed, then it will
 * suffice just to check sortBy.
 */

const isSortable = (column, searchDescriptor) => {
  const {
    sortBy
  } = column;
  return sortBy && (!searchDescriptor.getIn(['searchQuery', 'rel']) || sortBy.indexOf('/0/') === -1);
};

const rowRenderer = (params, location) => {
  // This is a fork of react-virtualized's default row renderer:
  // https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultRowRenderer.js
  const {
    className,
    columns,
    index,
    key,
    onRowClick,
    // onRowDoubleClick,
    // onRowMouseOut,
    // onRowMouseOver,
    // onRowRightClick,
    rowData,
    style
  } = params;
  const a11yProps = {};

  if (onRowClick // ||
  // onRowDoubleClick ||
  // onRowMouseOut ||
  // onRowMouseOver ||
  // onRowRightClick
  ) {
      a11yProps['aria-label'] = 'row';
      a11yProps.tabIndex = 0;

      if (onRowClick) {
        a11yProps.onClick = event => onRowClick({
          event,
          index,
          rowData
        });
      } // if (onRowDoubleClick) {
      //   a11yProps.onDoubleClick = event =>
      //     onRowDoubleClick({ event, index, rowData });
      // }
      // if (onRowMouseOut) {
      //   a11yProps.onMouseOut = event => onRowMouseOut({ event, index, rowData });
      // }
      // if (onRowMouseOver) {
      //   a11yProps.onMouseOver = event => onRowMouseOver({ event, index, rowData });
      // }
      // if (onRowRightClick) {
      //   a11yProps.onContextMenu = event =>
      //     onRowRightClick({ event, index, rowData });
      // }

    }

  if (location) {
    return /*#__PURE__*/_react.default.createElement(_reactRouterDom.Link, _extends({}, a11yProps, {
      className: className,
      key: key,
      role: "row",
      style: style,
      to: location
    }), columns);
  }

  return /*#__PURE__*/_react.default.createElement("div", _extends({}, a11yProps, {
    className: className,
    "data-index": index,
    key: key,
    role: "row",
    style: style
  }), columns);
};

const propTypes = {
  columnSetName: _propTypes.default.string,
  config: _propTypes.default.shape({
    listTypes: _propTypes.default.object,
    recordTypes: _propTypes.default.object,
    subresources: _propTypes.default.object
  }).isRequired,
  formatCellData: _propTypes.default.func,
  formatColumnLabel: _propTypes.default.func,
  isSearchPending: _propTypes.default.bool,
  linkItems: _propTypes.default.bool,
  // eslint-disable-next-line react/forbid-prop-types
  linkState: _propTypes.default.object,
  listType: _propTypes.default.string,
  perms: _propTypes.default.instanceOf(_immutable.default.Map),
  searchDescriptor: _propTypes.default.instanceOf(_immutable.default.Map),
  searchError: _propTypes.default.instanceOf(_immutable.default.Map),
  searchResult: _propTypes.default.instanceOf(_immutable.default.Map),
  showCheckboxColumn: _propTypes.default.bool,
  renderCheckbox: _propTypes.default.func,
  renderHeader: _propTypes.default.func,
  renderFooter: _propTypes.default.func,
  renderSelectBar: _propTypes.default.func,
  getItemLocation: _propTypes.default.func,
  onItemClick: _propTypes.default.func,
  onSortChange: _propTypes.default.func
};
const defaultProps = {
  columnSetName: 'default',
  formatCellData: (column, data) => data,
  formatColumnLabel: column => (0, _get.default)(column, ['messages', 'label', 'defaultMessage']),
  linkItems: true,
  listType: 'common',
  renderHeader: () => null,
  renderFooter: () => null,
  renderSelectBar: () => null
};

class SearchResultTable extends _react.Component {
  constructor() {
    super();
    this.getItemLocation = this.getItemLocation.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleRowClick = this.handleRowClick.bind(this);
    this.renderNoItems = this.renderNoItems.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.sort = this.sort.bind(this);
  }

  getItemLocation(item) {
    const {
      config,
      linkState,
      listType,
      perms,
      searchDescriptor
    } = this.props;
    const getItemLocationPath = (0, _get.default)(config, ['listTypes', listType, 'getItemLocationPath']);

    if (!getItemLocationPath) {
      return undefined;
    }

    const itemContext = {
      config,
      perms,
      searchDescriptor
    };
    const itemLocationPath = getItemLocationPath(item, itemContext);

    if (!itemLocationPath) {
      return undefined;
    } // Create a location with the item location path, along with enough state to reproduce this
    // search. The search descriptor is converted to an object in order to reliably store it in
    // location state. Also merge in any object that was passed in via the linkState prop.


    const state = {
      searchDescriptor: searchDescriptor.toJS(),
      // The search traverser on records will always link to the search result page, so use
      // its search name.
      searchName: 'searchResultPage',
      ...linkState
    };
    return {
      state,
      pathname: itemLocationPath
    };
  }

  handleKeyDown(event) {
    if (event.key === 'Enter') {
      const index = (0, _get.default)(event, ['target', 'dataset', 'index']);

      if (typeof index !== 'undefined') {
        this.handleRowClick(event.target.dataset.index);
      }
    }
  }

  handleRowClick(index) {
    const {
      config,
      listType,
      searchResult,
      onItemClick
    } = this.props;

    if (onItemClick) {
      const listTypeConfig = config.listTypes[listType];
      const {
        listNodeName,
        itemNodeName
      } = listTypeConfig;
      const items = searchResult.getIn([listNodeName, itemNodeName]);
      const item = _immutable.default.List.isList(items) ? items.get(index) : items;
      onItemClick(item, index);
    }
  }

  sort({
    sortBy,
    sortDirection
  }) {
    const {
      onSortChange
    } = this.props;

    if (onSortChange) {
      onSortChange(sortBy + (sortDirection === _cspaceLayout.Table.SortDirection.DESC ? ' desc' : ''));
    }
  }

  renderNoItems() {
    const {
      isSearchPending
    } = this.props;
    const message = isSearchPending ? /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, messages.searchPending) : null;
    return /*#__PURE__*/_react.default.createElement("div", {
      className: _SearchResultEmpty.default.common
    }, message);
  }

  renderRow(params) {
    const {
      getItemLocation,
      linkItems
    } = this.props;
    const {
      rowData
    } = params;
    let location;

    if (linkItems) {
      const locationGetter = getItemLocation || this.getItemLocation;
      location = locationGetter(rowData);
    }

    return rowRenderer(params, location);
  }

  renderTable() {
    const {
      columnSetName,
      config,
      formatCellData,
      formatColumnLabel,
      listType,
      searchDescriptor,
      searchResult,
      showCheckboxColumn,
      renderCheckbox
    } = this.props;

    if (searchResult) {
      const recordType = searchDescriptor.get('recordType');
      const subresource = searchDescriptor.get('subresource');
      const searchQuery = searchDescriptor.get('searchQuery');
      const listTypeConfig = config.listTypes[listType];
      const {
        listNodeName,
        itemNodeName
      } = listTypeConfig;
      let sortColumnName = null;
      let sortDir = null;
      const sortSpec = searchQuery.get('sort');

      if (sortSpec) {
        [sortColumnName, sortDir] = sortSpec.split(' ');
      }

      const list = searchResult.get(listNodeName);
      const pageSize = parseInt(list.get('pageSize'), 10);
      const totalItems = parseInt(list.get('totalItems'), 10);
      const itemsInPage = parseInt(list.get('itemsInPage'), 10);
      let items = list.get(itemNodeName);

      if (!items) {
        items = _immutable.default.List();
      }

      if (!_immutable.default.List.isList(items)) {
        // If there's only one result, it won't be returned as a list.
        items = _immutable.default.List.of(items);
      }

      const columnConfigurer = subresource ? config.subresources[subresource] : config.recordTypes[recordType];
      let columnConfig = (0, _get.default)(columnConfigurer, ['columns', columnSetName]);

      if (!columnConfig && columnSetName !== defaultProps.columnSetName) {
        // Fall back to the default column set if the named one doesn't exist.
        columnConfig = (0, _get.default)(columnConfigurer, ['columns', defaultProps.columnSetName]);
      }

      if (!columnConfig) {
        columnConfig = [];
      }

      const columns = Object.keys(columnConfig).filter(name => !columnConfig[name].disabled).sort((nameA, nameB) => {
        const orderA = columnConfig[nameA].order;
        const orderB = columnConfig[nameB].order;
        return orderA - orderB;
      }).map(name => {
        const column = columnConfig[name];
        return {
          cellDataGetter: ({
            dataKey,
            rowData
          }) => {
            let data = null;

            if (rowData) {
              const keys = dataKey.split('|');

              for (let i = 0; i < keys.length; i += 1) {
                const candidateValue = rowData.get(keys[i]);

                if (candidateValue) {
                  data = candidateValue;
                  break;
                }
              }
            }

            return formatCellData(column, data, rowData);
          },
          disableSort: !isSortable(column, searchDescriptor),
          flexGrow: column.flexGrow,
          flexShrink: column.flexShrink,
          label: formatColumnLabel(column),
          dataKey: column.dataKey || name,
          width: column.width
        };
      });
      let heightBasis;

      if (Number.isNaN(totalItems)) {
        // We don't yet know how many items are found by the search. Set the height to one item, so
        // an ellipsis (or other calculating indicator) can be shown.
        heightBasis = 1;
      } else {
        // If all of the search results fit on one page, shrink the table to fit the number of
        // results. Otherwise, size the table to fit the desired page size, even if there aren't
        // that many results on this page. This keeps the pager from jumping up on the last page
        // and while page/sorting changes are in progress.
        heightBasis = totalItems <= pageSize && !Number.isNaN(itemsInPage) ? itemsInPage : pageSize;

        if (heightBasis === 0) {
          // If there are no items, set the height to one, because it looks weird when the footer
          // is mashed up against the header. This also leaves room to display a "no records found"
          // message if desired.
          heightBasis = 1;
        }
      }

      const height = heightBasis * rowHeight + rowHeight;
      return /*#__PURE__*/_react.default.createElement("div", {
        style: {
          height
        }
      }, /*#__PURE__*/_react.default.createElement(_cspaceLayout.Table, {
        columns: columns,
        rowCount: items.size,
        rowGetter: ({
          index
        }) => items.get(index),
        showCheckboxColumn: showCheckboxColumn,
        onRowClick: this.handleRowClick,
        renderCheckbox: renderCheckbox,
        sort: this.sort,
        sortBy: sortColumnName,
        sortDirection: sortDir === 'desc' ? _cspaceLayout.Table.SortDirection.DESC : _cspaceLayout.Table.SortDirection.ASC,
        noRowsRenderer: this.renderNoItems,
        rowRenderer: this.renderRow
      }));
    }

    return null;
  }

  render() {
    const {
      isSearchPending,
      searchError,
      searchResult,
      renderHeader,
      renderSelectBar,
      renderFooter
    } = this.props;
    return (
      /*#__PURE__*/
      // eslint-disable-next-line jsx-a11y/no-static-element-interactions
      _react.default.createElement("div", {
        className: _SearchResultTable.default.common,
        role: "presentation",
        onKeyDown: this.handleKeyDown
      }, renderHeader({
        isSearchPending,
        searchError,
        searchResult
      }), renderSelectBar(), this.renderTable(), renderFooter({
        isSearchPending,
        searchError,
        searchResult
      }))
    );
  }

}

exports.default = SearchResultTable;
SearchResultTable.propTypes = propTypes;
SearchResultTable.defaultProps = defaultProps;