"use strict";

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

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

var _reactWindow = require("react-window");

var _reactVirtualizedAutoSizer = _interopRequireDefault(require("react-virtualized-auto-sizer"));

var _index = require("../styles/index.js");

var _index2 = require("../tag/index.js");

var _headerCell = _interopRequireDefault(require("./header-cell.js"));

var _constants = require("./constants.js");

var _measureColumnWidths = _interopRequireDefault(require("./measure-column-widths.js"));

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

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

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { 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 ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function CellPlacement(_ref) {
  var columnIndex = _ref.columnIndex,
      rowIndex = _ref.rowIndex,
      data = _ref.data,
      style = _ref.style;

  var _useStyletron = (0, _index.useStyletron)(),
      _useStyletron2 = _slicedToArray(_useStyletron, 2),
      useCss = _useStyletron2[0],
      theme = _useStyletron2[1];

  var cellStyle = useCss(_objectSpread({}, theme.borders.border200, {
    alignItems: 'center',
    backgroundColor: rowIndex % 2 ? null : theme.colors.mono200,
    borderTop: 'none',
    borderBottom: 'none',
    borderLeft: 'none',
    boxSizing: 'border-box',
    display: 'flex'
  })); // ignores the table header row

  if (rowIndex === 0) {
    return null;
  }

  var column = data.columns[columnIndex];
  var Cell = column.renderCell; // minus one to account for additional header row

  var value = data.rows[rowIndex - 1].data[columnIndex];
  return React.createElement("div", {
    className: cellStyle,
    style: style
  }, React.createElement(Cell, {
    value: value
  }));
}

function useDuplicateColumnTitleWarning(columns) {
  React.useEffect(function () {
    var titles = columns.reduce(function (set, column) {
      return set.add(column.title);
    }, new Set());

    if (titles.size < columns.length) {
      console.warn('Columns titles must be unique else will result in non-deterministic filtering.');
    }
  }, [columns]);
}

function useSortParameters() {
  var _React$useState = React.useState(-1),
      _React$useState2 = _slicedToArray(_React$useState, 2),
      sortIndex = _React$useState2[0],
      setSortIndex = _React$useState2[1];

  var _React$useState3 = React.useState(null),
      _React$useState4 = _slicedToArray(_React$useState3, 2),
      sortDirection = _React$useState4[0],
      setSortDirection = _React$useState4[1];

  function handleSort(columnIndex) {
    if (columnIndex === sortIndex) {
      if (sortDirection === _constants.SORT_DIRECTIONS.ASC) {
        setSortIndex(-1);
        setSortDirection(_constants.SORT_DIRECTIONS.DESC);
      } else {
        setSortDirection(_constants.SORT_DIRECTIONS.ASC);
      }
    } else {
      setSortIndex(columnIndex);
      setSortDirection(_constants.SORT_DIRECTIONS.DESC);
    }
  }

  return [sortIndex, sortDirection, handleSort];
}

var HeaderContext = React.createContext({
  addFilter: function addFilter() {},
  columns: [],
  filterOpenIndex: -1,
  setFilterOpenIndex: function setFilterOpenIndex() {},
  handleSort: function handleSort() {},
  headerHoverIndex: -1,
  setHeaderHoverIndex: function setHeaderHoverIndex() {},
  rows: [],
  sortIndex: -1,
  sortDirection: null,
  widths: []
});
HeaderContext.displayName = 'HeaderContext'; // replaces the content of the virtualized window with contents. in this case,
// we are prepending a table header row before the table rows (children to the fn).

var InnerTableElement = React.forwardRef(function (props, ref) {
  var _useStyletron3 = (0, _index.useStyletron)(),
      _useStyletron4 = _slicedToArray(_useStyletron3, 2),
      useCss = _useStyletron4[0],
      theme = _useStyletron4[1];

  var ctx = React.useContext(HeaderContext); // no need to render the cells until the columns have been measured

  if (!ctx.widths.filter(Boolean).length) {
    return null;
  }

  var headerStyle = useCss(_objectSpread({}, theme.borders.border200, {
    backgroundColor: theme.colors.mono100,
    borderTop: 'none',
    borderLeft: 'none',
    boxSizing: 'border-box'
  }));
  return React.createElement("div", {
    ref: ref,
    "data-baseweb": "data-table",
    style: props.style
  }, React.createElement("div", {
    className: useCss({
      position: 'sticky',
      top: 0,
      left: 0,
      width: "".concat(ctx.widths.reduce(function (sum, w) {
        return sum + w;
      }, 0), "px"),
      height: '48px',
      display: 'flex',
      // this feels bad.. the absolutely positioned children elements
      // stack on top of this element with the layer component.
      zIndex: 2
    })
  }, ctx.columns.map(function (column, columnIndex) {
    var width = ctx.widths[columnIndex];
    return React.createElement("div", {
      className: headerStyle,
      key: columnIndex,
      style: {
        width: width
      }
    }, React.createElement(_headerCell["default"], {
      index: columnIndex,
      filterable: column.filterable,
      sortable: column.sortable,
      isHovered: ctx.headerHoverIndex === columnIndex,
      isFilterOpen: ctx.filterOpenIndex === columnIndex,
      onFilterOpen: function onFilterOpen() {
        if (ctx.filterOpenIndex === columnIndex) {
          ctx.setFilterOpenIndex(-1);
        } else {
          ctx.setFilterOpenIndex(columnIndex);
        }
      },
      onFilterClose: function onFilterClose() {
        return ctx.setFilterOpenIndex(-1);
      },
      onMouseEnter: function onMouseEnter() {
        ctx.setHeaderHoverIndex(columnIndex);

        if (columnIndex !== ctx.filterOpenIndex) {
          ctx.setFilterOpenIndex(-1);
        }
      },
      onMouseLeave: function onMouseLeave() {
        return ctx.setHeaderHoverIndex(-1);
      },
      onSort: ctx.handleSort,
      filter: function filter(_ref2) {
        var close = _ref2.close;
        var Filter = column.renderFilter;
        return React.createElement(Filter, {
          setFilter: function setFilter(filterParams, description) {
            ctx.addFilter(filterParams, column.title, description);
          },
          data: ctx.rows.map(function (r) {
            return r.data[columnIndex];
          }),
          close: close
        });
      },
      sortDirection: ctx.sortIndex === columnIndex ? ctx.sortDirection : null,
      title: column.title
    }));
  })), props.children);
});
InnerTableElement.displayName = 'InnerTableElement';

function Unstable_DataTable(props) {
  useDuplicateColumnTitleWarning(props.columns);

  var _useSortParameters = useSortParameters(),
      _useSortParameters2 = _slicedToArray(_useSortParameters, 3),
      sortIndex = _useSortParameters2[0],
      sortDirection = _useSortParameters2[1],
      handleSort = _useSortParameters2[2];

  var _React$useState5 = React.useState(new Map()),
      _React$useState6 = _slicedToArray(_React$useState5, 2),
      filters = _React$useState6[0],
      setFilters = _React$useState6[1];

  var _React$useState7 = React.useState(props.columns.map(function () {
    return 0;
  })),
      _React$useState8 = _slicedToArray(_React$useState7, 2),
      widths = _React$useState8[0],
      setWidths = _React$useState8[1];

  var gridRef = React.useRef(null); // indicates which header cell is currently hovered

  var _React$useState9 = React.useState(-1),
      _React$useState10 = _slicedToArray(_React$useState9, 2),
      headerHoverIndex = _React$useState10[0],
      setHeaderHoverIndex = _React$useState10[1]; // filter open state tracked outside of header cell so that mouse-leave from the header
  // does not cause the popover to close.


  var _React$useState11 = React.useState(-1),
      _React$useState12 = _slicedToArray(_React$useState11, 2),
      filterOpenIndex = _React$useState12[0],
      setFilterOpenIndex = _React$useState12[1];

  function addFilter(filterParams, title, description) {
    filters.set(title, {
      filterParams: filterParams,
      description: description
    });
    setFilters(new Map(filters));
  }

  function removeFilter(title) {
    filters["delete"](title);
    setFilters(new Map(filters));
  }

  var sortedIndices = React.useMemo(function () {
    var toSort = props.rows.map(function (r, i) {
      return [r, i];
    });

    if (sortIndex !== -1) {
      var sortFn = props.columns[sortIndex].sortFn;

      if (sortDirection === _constants.SORT_DIRECTIONS.DESC) {
        toSort.sort(function (a, b) {
          return sortFn(a[0].data[sortIndex], b[0].data[sortIndex]);
        });
      } else if (sortDirection === _constants.SORT_DIRECTIONS.ASC) {
        toSort.sort(function (a, b) {
          return sortFn(b[0].data[sortIndex], a[0].data[sortIndex]);
        });
      }
    }

    return toSort.map(function (el) {
      return el[1];
    });
  }, [sortIndex, sortDirection, props.columns, props.rows]);
  var filteredIndices = React.useMemo(function () {
    var set = new Set(props.rows.map(function (_, idx) {
      return idx;
    }));
    Array.from(filters, function (f) {
      return f;
    }).forEach(function (_ref3) {
      var _ref4 = _slicedToArray(_ref3, 2),
          title = _ref4[0],
          filter = _ref4[1];

      var columnIndex = props.columns.findIndex(function (c) {
        return c.title === title;
      });
      var column = props.columns[columnIndex];

      if (!column) {
        return;
      }

      var filterFn = column.buildFilter(filter.filterParams);
      Array.from(set).forEach(function (idx) {
        if (!filterFn(props.rows[idx].data[columnIndex])) {
          set["delete"](idx);
        }
      });
    });
    return set;
  }, [filters, props.columns, props.rows]);
  var rows = React.useMemo(function () {
    return sortedIndices.filter(function (idx) {
      return filteredIndices.has(idx);
    }).map(function (idx) {
      return props.rows[idx];
    });
  }, [sortedIndices, filteredIndices, props.rows]);
  return React.createElement(React.Fragment, null, React.createElement(_measureColumnWidths["default"], {
    columns: props.columns,
    rows: props.rows,
    widths: widths,
    onWidthsChange: function onWidthsChange(nextWidths) {
      setWidths(nextWidths);

      if (gridRef.current) {
        // $FlowFixMe trigger react-window to layout the elements again
        gridRef.current.resetAfterColumnIndex(0, true);
      }
    }
  }), Array.from(filters).map(function (_ref5) {
    var _ref6 = _slicedToArray(_ref5, 2),
        title = _ref6[0],
        filter = _ref6[1];

    return React.createElement(_index2.Tag, {
      key: title,
      onActionClick: function onActionClick() {
        return removeFilter(title);
      }
    }, title, " | ", filter.description);
  }), React.createElement(_reactVirtualizedAutoSizer["default"], null, function (_ref7) {
    var height = _ref7.height,
        width = _ref7.width;
    return React.createElement(HeaderContext.Provider, {
      value: {
        columns: props.columns,
        rows: props.rows,
        addFilter: addFilter,
        filterOpenIndex: filterOpenIndex,
        setFilterOpenIndex: setFilterOpenIndex,
        handleSort: handleSort,
        headerHoverIndex: headerHoverIndex,
        setHeaderHoverIndex: setHeaderHoverIndex,
        sortDirection: sortDirection,
        sortIndex: sortIndex,
        widths: widths
      }
    }, React.createElement(_reactWindow.VariableSizeGrid // eslint-disable-next-line flowtype/no-weak-types
    , {
      ref: gridRef,
      overscanRowCount: 10,
      innerElementType: InnerTableElement,
      columnCount: props.columns.length,
      columnWidth: function columnWidth(columnIndex) {
        return widths[columnIndex];
      },
      height: height // plus one to account for additional header row
      ,
      rowCount: rows.length + 1,
      rowHeight: function rowHeight(rowIndex) {
        return rowIndex === 0 ? 48 : 40;
      },
      width: width,
      itemData: {
        columns: props.columns,
        rows: rows
      }
    }, CellPlacement));
  }));
}