"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("../button/index.js");

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

var _index3 = require("../tooltip/index.js");

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

var _headerCell = _interopRequireDefault(require("./header-cell.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; }

// consider pulling this out to a prop if useful.
var HEADER_ROW_HEIGHT = 48;

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

  var _useStyletron = (0, _index2.useStyletron)(),
      _useStyletron2 = _slicedToArray(_useStyletron, 2),
      css = _useStyletron2[0],
      theme = _useStyletron2[1]; // ignores the table header row


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

  var backgroundColor = theme.colors.mono100;

  if (rowIndex % 2 && columnIndex === data.columnHoverIndex || rowIndex === data.rowHoverIndex) {
    backgroundColor = theme.colors.mono300;
  } else if (rowIndex % 2 || columnIndex === data.columnHoverIndex) {
    backgroundColor = theme.colors.mono200;
  }

  var Cell = data.columns[columnIndex].renderCell;
  var value = data.columns[columnIndex].mapDataToValue(data.rows[rowIndex - 1].data);
  return React.createElement("div", {
    className: css(_objectSpread({}, theme.borders.border200, {
      backgroundColor: backgroundColor,
      borderTop: 'none',
      borderBottom: 'none',
      borderLeft: 'none',
      boxSizing: 'border-box'
    })),
    style: style,
    onMouseEnter: function onMouseEnter() {
      return data.onHoverRow(rowIndex);
    }
  }, React.createElement(Cell, {
    value: value,
    onSelect: data.isSelectable && columnIndex === 0 ? function () {
      return data.onSelectOne(data.rows[rowIndex - 1]);
    } : undefined,
    isSelected: data.isRowSelected(data.rows[rowIndex - 1].id),
    textQuery: data.textQuery
  }));
}

function compareCellPlacement(prevProps, nextProps) {
  // header cells are not rendered through this component
  if (prevProps.rowIndex === 0) {
    return true;
  }

  if (prevProps.data.columns !== nextProps.data.columns || prevProps.data.rows !== nextProps.data.rows || prevProps.style !== nextProps.style) {
    return false;
  }

  if (prevProps.data.isSelectable === nextProps.data.isSelectable && prevProps.data.columnHoverIndex === nextProps.data.columnHoverIndex && prevProps.data.rowHoverIndex === nextProps.data.rowHoverIndex && prevProps.data.textQuery === nextProps.data.textQuery && prevProps.data.isRowSelected === nextProps.data.isRowSelected) {
    return true;
  } // at this point we know that the rowHoverIndex or the columnHoverIndex has changed.
  // row does not need to re-render if not transitioning _from_ or _to_ highlighted
  // also ensures that all cells are invalidated on column-header hover


  if (prevProps.rowIndex !== prevProps.data.rowHoverIndex && prevProps.rowIndex !== nextProps.data.rowHoverIndex && prevProps.data.columnHoverIndex === nextProps.data.columnHoverIndex && prevProps.data.isRowSelected === nextProps.data.isRowSelected) {
    return true;
  } // similar to the row highlight optimization, do not update the cell if not in the previously
  // highlighted column or next highlighted.


  if (prevProps.columnIndex !== prevProps.data.columnHoverIndex && prevProps.columnIndex !== nextProps.data.columnHoverIndex && prevProps.data.rowHoverIndex === nextProps.data.rowHoverIndex && prevProps.data.isRowSelected === nextProps.data.isRowSelected) {
    return true;
  }

  return false;
}

var CellPlacementMemo = React.memo(CellPlacement, compareCellPlacement);
CellPlacementMemo.displayName = 'CellPlacement';
var HeaderContext = React.createContext({
  columns: [],
  columnHoverIndex: -1,
  filters: new Map(),
  isScrollingX: false,
  isSelectable: false,
  isSelectedAll: false,
  isSelectedIndeterminate: false,
  onMouseEnter: function onMouseEnter() {},
  onMouseLeave: function onMouseLeave() {},
  onSelectMany: function onSelectMany() {},
  onSelectNone: function onSelectNone() {},
  onSort: function onSort() {},
  rowActions: [],
  rowHeight: 0,
  rowHoverIndex: -1,
  rows: [],
  scrollLeft: 0,
  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, _index2.useStyletron)(),
      _useStyletron4 = _slicedToArray(_useStyletron3, 2),
      css = _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;
  }

  return React.createElement("div", {
    ref: ref,
    "data-baseweb": "data-table",
    style: props.style
  }, React.createElement("div", {
    className: css({
      position: 'sticky',
      top: 0,
      left: 0,
      width: "".concat(ctx.widths.reduce(function (sum, w) {
        return sum + w;
      }, 0), "px"),
      height: "".concat(HEADER_ROW_HEIGHT, "px"),
      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 activeFilter = ctx.filters ? ctx.filters.get(column.title) : null;
    return React.createElement(_index3.Tooltip, {
      key: columnIndex,
      placement: _index3.PLACEMENT.bottomLeft,
      isOpen: ctx.columnHoverIndex === columnIndex && Boolean(activeFilter),
      content: function content() {
        return React.createElement("div", null, React.createElement("p", {
          className: css(_objectSpread({}, theme.typography.font100, {
            color: theme.colors.foregroundInv
          }))
        }, "filter applied to ", column.title), activeFilter && React.createElement("p", {
          className: css(_objectSpread({}, theme.typography.font150, {
            color: theme.colors.foregroundInv
          }))
        }, activeFilter.description));
      }
    }, React.createElement("div", {
      className: css(_objectSpread({}, theme.borders.border200, {
        backgroundColor: theme.colors.mono100,
        borderTop: 'none',
        borderLeft: 'none',
        boxSizing: 'border-box'
      })),
      style: {
        width: ctx.widths[columnIndex]
      }
    }, React.createElement(_headerCell.default, {
      index: columnIndex,
      sortable: column.sortable,
      isHovered: ctx.columnHoverIndex === columnIndex,
      isSelectable: ctx.isSelectable && columnIndex === 0,
      isSelectedAll: ctx.isSelectedAll,
      isSelectedIndeterminate: ctx.isSelectedIndeterminate,
      onMouseEnter: function onMouseEnter() {
        return ctx.onMouseEnter(columnIndex);
      },
      onMouseLeave: function onMouseLeave() {
        return ctx.onMouseLeave();
      },
      onSelectAll: ctx.onSelectMany,
      onSelectNone: ctx.onSelectNone,
      onSort: ctx.onSort,
      sortDirection: ctx.sortIndex === columnIndex ? ctx.sortDirection : null,
      title: column.title
    })));
  })), React.Children.toArray(props.children).length <= ctx.columns.length ? React.createElement("div", {
    className: css(_objectSpread({}, theme.typography.font100, {
      marginTop: theme.sizing.scale600,
      marginLeft: theme.sizing.scale600
    }))
  }, "No rows match the filter criteria defined. Please remove one or more filters to view more data.") : props.children, ctx.rowActions && Boolean(ctx.rowActions.length) && ctx.rowHoverIndex > 0 && !ctx.isScrollingX && React.createElement("div", {
    style: {
      alignItems: 'center',
      backgroundColor: 'rgba(238, 238, 238, 0.99)',
      display: 'flex',
      height: "".concat(ctx.rowHeight, "px"),
      padding: '0 16px',
      paddingLeft: theme.sizing.scale300,
      paddingRight: theme.sizing.scale300,
      position: 'absolute',
      right: 0 - ctx.scrollLeft,
      top: (ctx.rowHoverIndex - 1) * ctx.rowHeight + HEADER_ROW_HEIGHT
    }
  }, ctx.rowActions.map(function (rowAction) {
    var RowActionIcon = rowAction.renderIcon;
    return React.createElement(_index.Button, {
      alt: rowAction.label,
      key: rowAction.label,
      onClick: function onClick(event) {
        return rowAction.onClick({
          event: event,
          row: ctx.rows[ctx.rowHoverIndex - 1]
        });
      },
      size: _index.SIZE.compact,
      kind: _index.KIND.minimal,
      shape: _index.SHAPE.round,
      overrides: {
        BaseButton: {
          style: {
            marginLeft: theme.sizing.scale300
          }
        }
      }
    }, React.createElement(RowActionIcon, {
      size: 24
    }));
  })));
});
InnerTableElement.displayName = 'InnerTableElement';

function Unstable_DataTable(props) {
  var _useStyletron5 = (0, _index2.useStyletron)(),
      _useStyletron6 = _slicedToArray(_useStyletron5, 2),
      theme = _useStyletron6[1];

  var _rowHeight = props.rowHeight || 36;

  var gridRef = React.useRef(null);

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

  var handleWidthsChange = React.useCallback(function (nextWidths) {
    setWidths(nextWidths);

    if (gridRef.current) {
      // $FlowFixMe trigger react-window to layout the elements again
      gridRef.current.resetAfterColumnIndex(0, true);
    }
  }, [gridRef.current]);

  var _React$useState3 = React.useState(0),
      _React$useState4 = _slicedToArray(_React$useState3, 2),
      scrollLeft = _React$useState4[0],
      setScrollLeft = _React$useState4[1];

  var _React$useState5 = React.useState(false),
      _React$useState6 = _slicedToArray(_React$useState5, 2),
      isScrollingX = _React$useState6[0],
      setIsScrollingX = _React$useState6[1];

  var _React$useState7 = React.useState(false),
      _React$useState8 = _slicedToArray(_React$useState7, 2),
      recentlyScrolledX = _React$useState8[0],
      setRecentlyScrolledX = _React$useState8[1];

  React.useLayoutEffect(function () {
    if (recentlyScrolledX !== isScrollingX) {
      setIsScrollingX(recentlyScrolledX);
    }

    if (recentlyScrolledX) {
      var timeout = setTimeout(function () {
        setRecentlyScrolledX(false);
      }, 200);
      return function () {
        return clearTimeout(timeout);
      };
    }
  }, [recentlyScrolledX]);
  var handleScroll = React.useCallback(function (params) {
    setScrollLeft(params.scrollLeft);

    if (params.scrollLeft !== scrollLeft) {
      setRecentlyScrolledX(true);
    }
  }, [scrollLeft, setScrollLeft, setRecentlyScrolledX]);

  var _React$useState9 = React.useState(-1),
      _React$useState10 = _slicedToArray(_React$useState9, 2),
      rowHoverIndex = _React$useState10[0],
      setRowHoverIndex = _React$useState10[1];

  var handleRowHover = React.useCallback(function (nextIndex) {
    setColumnHoverIndex(-1);

    if (nextIndex !== rowHoverIndex) {
      setRowHoverIndex(nextIndex);
    }
  }, [rowHoverIndex]);

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

  function handleColumnHeaderMouseEnter(columnIndex) {
    setColumnHoverIndex(columnIndex);
    setRowHoverIndex(-1);
  }

  function handleColumnHeaderMouseLeave() {
    // $FlowFixMe - unable to get the state type from react-window
    if (gridRef.current && !gridRef.current.state.isScrolling) {
      setColumnHoverIndex(-1);
    }
  }

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

    if (index !== null && index !== undefined && index !== -1) {
      var sortFn = props.columns[index].sortFn;

      var getValue = function getValue(row) {
        return props.columns[index].mapDataToValue(row.data);
      };

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

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

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

      if (!column) {
        return;
      } // start here after


      var filterFn = column.buildFilter(filter);
      Array.from(set).forEach(function (idx) {
        if (!filterFn(column.mapDataToValue(props.rows[idx].data))) {
          set.delete(idx);
        }
      });
    });

    if (textQuery) {
      var stringishColumnIndices = [];

      for (var i = 0; i < props.columns.length; i++) {
        if (props.columns[i].kind === _constants.COLUMNS.CATEGORICAL || props.columns[i].kind === _constants.COLUMNS.STRING) {
          stringishColumnIndices.push(i);
        }
      }

      Array.from(set).forEach(function (idx) {
        var matches = stringishColumnIndices.some(function (cdx) {
          var column = props.columns[cdx];
          return column.mapDataToValue(props.rows[idx].data).toLowerCase().includes(textQuery);
        });

        if (!matches) {
          set.delete(idx);
        }
      });
    }

    return set;
  }, [props.filters, textQuery, 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]);
  var isSelectable = props.batchActions ? !!props.batchActions.length : false;
  var isSelectedAll = React.useMemo(function () {
    if (!props.selectedRowIds) {
      return false;
    }

    return !!rows.length && props.selectedRowIds.size >= rows.length;
  }, [props.selectedRowIds, rows.length]);
  var isSelectedIndeterminate = React.useMemo(function () {
    if (!props.selectedRowIds) {
      return false;
    }

    return !!props.selectedRowIds.size && props.selectedRowIds.size < rows.length;
  }, [props.selectedRowIds, rows.length]);
  var isRowSelected = React.useCallback(function (id) {
    if (props.selectedRowIds) {
      return props.selectedRowIds.has(id);
    }

    return false;
  }, [props.selectedRowIds]);
  var handleSelectMany = React.useCallback(function () {
    if (props.onSelectMany) {
      props.onSelectMany(rows);
    }
  }, [rows, props.onSelectMany]);
  var handleSelectNone = React.useCallback(function () {
    if (props.onSelectNone) {
      props.onSelectNone();
    }
  }, [props.onSelectNone]);
  var handleSelectOne = React.useCallback(function (row) {
    if (props.onSelectOne) {
      props.onSelectOne(row);
    }
  }, [props.onSelectOne]);
  var handleSort = React.useCallback(function (columnIndex) {
    if (props.onSort) {
      props.onSort(columnIndex);
    }
  }, [props.onSort]);
  var itemData = React.useMemo(function () {
    return {
      columnHoverIndex: columnHoverIndex,
      rowHoverIndex: rowHoverIndex,
      isRowSelected: isRowSelected,
      isSelectable: isSelectable,
      onHoverRow: handleRowHover,
      onSelectOne: handleSelectOne,
      columns: props.columns,
      rows: rows,
      textQuery: textQuery
    };
  }, [handleRowHover, columnHoverIndex, isRowSelected, isSelectable, rowHoverIndex, rows, props.columns, handleSelectOne, textQuery]);
  return React.createElement(React.Fragment, null, React.createElement(_measureColumnWidths.default, {
    columns: props.columns,
    rows: props.rows,
    widths: widths,
    isSelectable: isSelectable,
    onWidthsChange: handleWidthsChange
  }), React.createElement(_reactVirtualizedAutoSizer.default, null, function (_ref4) {
    var height = _ref4.height,
        width = _ref4.width;
    return React.createElement(HeaderContext.Provider, {
      value: {
        columns: props.columns,
        columnHoverIndex: columnHoverIndex,
        filters: props.filters,
        isScrollingX: isScrollingX,
        isSelectable: isSelectable,
        isSelectedAll: isSelectedAll,
        isSelectedIndeterminate: isSelectedIndeterminate,
        onMouseEnter: handleColumnHeaderMouseEnter,
        onMouseLeave: handleColumnHeaderMouseLeave,
        onSelectMany: handleSelectMany,
        onSelectNone: handleSelectNone,
        onSort: handleSort,
        rowActions: props.rowActions || [],
        rowHeight: _rowHeight,
        rowHoverIndex: rowHoverIndex,
        rows: rows,
        scrollLeft: scrollLeft,
        sortDirection: props.sortDirection || null,
        sortIndex: typeof props.sortIndex === 'number' ? props.sortIndex : -1,
        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 ? HEADER_ROW_HEIGHT : _rowHeight;
      },
      width: width,
      itemData: itemData,
      onScroll: handleScroll,
      style: _objectSpread({}, theme.borders.border200, {
        borderColor: theme.colors.mono500
      })
    }, CellPlacementMemo));
  }));
}