"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useGridRowSelection = exports.rowSelectionStateInitializer = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var React = _interopRequireWildcard(require("react"));
var _useGridApiEventHandler = require("../../utils/useGridApiEventHandler");
var _useGridApiMethod = require("../../utils/useGridApiMethod");
var _useGridLogger = require("../../utils/useGridLogger");
var _gridRowsSelector = require("../rows/gridRowsSelector");
var _gridRowSelectionSelector = require("./gridRowSelectionSelector");
var _pagination = require("../pagination");
var _gridFocusStateSelector = require("../focus/gridFocusStateSelector");
var _gridFilterSelector = require("../filter/gridFilterSelector");
var _colDef = require("../../../colDef");
var _gridEditRowModel = require("../../../models/gridEditRowModel");
var _keyboardUtils = require("../../../utils/keyboardUtils");
var _useGridVisibleRows = require("../../utils/useGridVisibleRows");
var _gridDetailPanelToggleField = require("../../../constants/gridDetailPanelToggleField");
var _gridClasses = require("../../../constants/gridClasses");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && 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; }
const getSelectionModelPropValue = (selectionModelProp, prevSelectionModel) => {
  if (selectionModelProp == null) {
    return selectionModelProp;
  }
  if (Array.isArray(selectionModelProp)) {
    return selectionModelProp;
  }
  if (prevSelectionModel && prevSelectionModel[0] === selectionModelProp) {
    return prevSelectionModel;
  }
  return [selectionModelProp];
};
const rowSelectionStateInitializer = (state, props) => (0, _extends2.default)({}, state, {
  rowSelection: props.rowSelection ? getSelectionModelPropValue(props.rowSelectionModel) ?? [] : []
});

/**
 * @requires useGridRows (state, method) - can be after
 * @requires useGridParamsApi (method) - can be after
 * @requires useGridFocus (state) - can be after
 * @requires useGridKeyboardNavigation (`cellKeyDown` event must first be consumed by it)
 */
exports.rowSelectionStateInitializer = rowSelectionStateInitializer;
const useGridRowSelection = (apiRef, props) => {
  const logger = (0, _useGridLogger.useGridLogger)(apiRef, 'useGridSelection');
  const runIfRowSelectionIsEnabled = callback => (...args) => {
    if (props.rowSelection) {
      callback(...args);
    }
  };
  const propRowSelectionModel = React.useMemo(() => {
    return getSelectionModelPropValue(props.rowSelectionModel, (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state));
  }, [apiRef, props.rowSelectionModel]);
  const lastRowToggled = React.useRef(null);
  apiRef.current.registerControlState({
    stateId: 'rowSelection',
    propModel: propRowSelectionModel,
    propOnChange: props.onRowSelectionModelChange,
    stateSelector: _gridRowSelectionSelector.gridRowSelectionStateSelector,
    changeEvent: 'rowSelectionChange'
  });
  const {
    checkboxSelection,
    disableMultipleRowSelection,
    disableRowSelectionOnClick,
    isRowSelectable: propIsRowSelectable
  } = props;
  const canHaveMultipleSelection = !disableMultipleRowSelection || checkboxSelection;
  const visibleRows = (0, _useGridVisibleRows.useGridVisibleRows)(apiRef, props);
  const expandMouseRowRangeSelection = React.useCallback(id => {
    let endId = id;
    const startId = lastRowToggled.current ?? id;
    const isSelected = apiRef.current.isRowSelected(id);
    if (isSelected) {
      const visibleRowIds = (0, _gridFilterSelector.gridExpandedSortedRowIdsSelector)(apiRef);
      const startIndex = visibleRowIds.findIndex(rowId => rowId === startId);
      const endIndex = visibleRowIds.findIndex(rowId => rowId === endId);
      if (startIndex === endIndex) {
        return;
      }
      if (startIndex > endIndex) {
        endId = visibleRowIds[endIndex + 1];
      } else {
        endId = visibleRowIds[endIndex - 1];
      }
    }
    lastRowToggled.current = id;
    apiRef.current.selectRowRange({
      startId,
      endId
    }, !isSelected);
  }, [apiRef]);

  /**
   * API METHODS
   */
  const setRowSelectionModel = React.useCallback(model => {
    if (props.signature === _useGridApiEventHandler.GridSignature.DataGrid && !props.checkboxSelection && Array.isArray(model) && model.length > 1) {
      throw new Error(['MUI: `rowSelectionModel` can only contain 1 item in DataGrid.', 'You need to upgrade to DataGridPro or DataGridPremium component to unlock multiple selection.'].join('\n'));
    }
    const currentModel = (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state);
    if (currentModel !== model) {
      logger.debug(`Setting selection model`);
      apiRef.current.setState(state => (0, _extends2.default)({}, state, {
        rowSelection: props.rowSelection ? model : []
      }));
      apiRef.current.forceUpdate();
    }
  }, [apiRef, logger, props.rowSelection, props.signature, props.checkboxSelection]);
  const isRowSelected = React.useCallback(id => (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state).includes(id), [apiRef]);
  const isRowSelectable = React.useCallback(id => {
    if (propIsRowSelectable && !propIsRowSelectable(apiRef.current.getRowParams(id))) {
      return false;
    }
    const rowNode = apiRef.current.getRowNode(id);
    if (rowNode?.type === 'footer' || rowNode?.type === 'pinnedRow') {
      return false;
    }
    return true;
  }, [apiRef, propIsRowSelectable]);
  const getSelectedRows = React.useCallback(() => (0, _gridRowSelectionSelector.selectedGridRowsSelector)(apiRef), [apiRef]);
  const selectRow = React.useCallback((id, isSelected = true, resetSelection = false) => {
    if (!apiRef.current.isRowSelectable(id)) {
      return;
    }
    lastRowToggled.current = id;
    if (resetSelection) {
      logger.debug(`Setting selection for row ${id}`);
      apiRef.current.setRowSelectionModel(isSelected ? [id] : []);
    } else {
      logger.debug(`Toggling selection for row ${id}`);
      const selection = (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state);
      const newSelection = selection.filter(el => el !== id);
      if (isSelected) {
        newSelection.push(id);
      }
      const isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
      if (isSelectionValid) {
        apiRef.current.setRowSelectionModel(newSelection);
      }
    }
  }, [apiRef, logger, canHaveMultipleSelection]);
  const selectRows = React.useCallback((ids, isSelected = true, resetSelection = false) => {
    logger.debug(`Setting selection for several rows`);
    const selectableIds = ids.filter(id => apiRef.current.isRowSelectable(id));
    let newSelection;
    if (resetSelection) {
      newSelection = isSelected ? selectableIds : [];
    } else {
      // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
      const selectionLookup = (0, _extends2.default)({}, (0, _gridRowSelectionSelector.selectedIdsLookupSelector)(apiRef));
      selectableIds.forEach(id => {
        if (isSelected) {
          selectionLookup[id] = id;
        } else {
          delete selectionLookup[id];
        }
      });
      newSelection = Object.values(selectionLookup);
    }
    const isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
    if (isSelectionValid) {
      apiRef.current.setRowSelectionModel(newSelection);
    }
  }, [apiRef, logger, canHaveMultipleSelection]);
  const selectRowRange = React.useCallback(({
    startId,
    endId
  }, isSelected = true, resetSelection = false) => {
    if (!apiRef.current.getRow(startId) || !apiRef.current.getRow(endId)) {
      return;
    }
    logger.debug(`Expanding selection from row ${startId} to row ${endId}`);

    // Using rows from all pages allow to select a range across several pages
    const allPagesRowIds = (0, _gridFilterSelector.gridExpandedSortedRowIdsSelector)(apiRef);
    const startIndex = allPagesRowIds.indexOf(startId);
    const endIndex = allPagesRowIds.indexOf(endId);
    const [start, end] = startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex];
    const rowsBetweenStartAndEnd = allPagesRowIds.slice(start, end + 1);
    apiRef.current.selectRows(rowsBetweenStartAndEnd, isSelected, resetSelection);
  }, [apiRef, logger]);
  const selectionPublicApi = {
    selectRow,
    setRowSelectionModel,
    getSelectedRows,
    isRowSelected,
    isRowSelectable
  };
  const selectionPrivateApi = {
    selectRows,
    selectRowRange
  };
  (0, _useGridApiMethod.useGridApiMethod)(apiRef, selectionPublicApi, 'public');
  (0, _useGridApiMethod.useGridApiMethod)(apiRef, selectionPrivateApi, props.signature === _useGridApiEventHandler.GridSignature.DataGrid ? 'private' : 'public');

  /**
   * EVENTS
   */
  const removeOutdatedSelection = React.useCallback(() => {
    if (props.keepNonExistentRowsSelected) {
      return;
    }
    const currentSelection = (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state);
    const rowsLookup = (0, _gridRowsSelector.gridRowsLookupSelector)(apiRef);

    // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
    const selectionLookup = (0, _extends2.default)({}, (0, _gridRowSelectionSelector.selectedIdsLookupSelector)(apiRef));
    let hasChanged = false;
    currentSelection.forEach(id => {
      if (!rowsLookup[id]) {
        delete selectionLookup[id];
        hasChanged = true;
      }
    });
    if (hasChanged) {
      apiRef.current.setRowSelectionModel(Object.values(selectionLookup));
    }
  }, [apiRef, props.keepNonExistentRowsSelected]);
  const handleSingleRowSelection = React.useCallback((id, event) => {
    const hasCtrlKey = event.metaKey || event.ctrlKey;

    // multiple selection is only allowed if:
    // - it is a checkboxSelection
    // - it is a keyboard selection
    // - Ctrl is pressed

    const isMultipleSelectionDisabled = !checkboxSelection && !hasCtrlKey && !(0, _keyboardUtils.isKeyboardEvent)(event);
    const resetSelection = !canHaveMultipleSelection || isMultipleSelectionDisabled;
    const isSelected = apiRef.current.isRowSelected(id);
    if (resetSelection) {
      apiRef.current.selectRow(id, !isMultipleSelectionDisabled ? !isSelected : true, true);
    } else {
      apiRef.current.selectRow(id, !isSelected, false);
    }
  }, [apiRef, canHaveMultipleSelection, checkboxSelection]);
  const handleRowClick = React.useCallback((params, event) => {
    if (disableRowSelectionOnClick) {
      return;
    }
    const field = event.target.closest(`.${_gridClasses.gridClasses.cell}`)?.getAttribute('data-field');
    if (field === _colDef.GRID_CHECKBOX_SELECTION_COL_DEF.field) {
      // click on checkbox should not trigger row selection
      return;
    }
    if (field === _gridDetailPanelToggleField.GRID_DETAIL_PANEL_TOGGLE_FIELD) {
      // click to open the detail panel should not select the row
      return;
    }
    if (field) {
      const column = apiRef.current.getColumn(field);
      if (column.type === _colDef.GRID_ACTIONS_COLUMN_TYPE) {
        return;
      }
    }
    const rowNode = apiRef.current.getRowNode(params.id);
    if (rowNode.type === 'pinnedRow') {
      return;
    }
    if (event.shiftKey && (canHaveMultipleSelection || checkboxSelection)) {
      expandMouseRowRangeSelection(params.id);
    } else {
      handleSingleRowSelection(params.id, event);
    }
  }, [disableRowSelectionOnClick, canHaveMultipleSelection, checkboxSelection, apiRef, expandMouseRowRangeSelection, handleSingleRowSelection]);
  const preventSelectionOnShift = React.useCallback((params, event) => {
    if (canHaveMultipleSelection && event.shiftKey) {
      window.getSelection()?.removeAllRanges();
    }
  }, [canHaveMultipleSelection]);
  const handleRowSelectionCheckboxChange = React.useCallback((params, event) => {
    if (event.nativeEvent.shiftKey) {
      expandMouseRowRangeSelection(params.id);
    } else {
      apiRef.current.selectRow(params.id, params.value);
    }
  }, [apiRef, expandMouseRowRangeSelection]);
  const handleHeaderSelectionCheckboxChange = React.useCallback(params => {
    const shouldLimitSelectionToCurrentPage = props.checkboxSelectionVisibleOnly && props.pagination;
    const rowsToBeSelected = shouldLimitSelectionToCurrentPage ? (0, _pagination.gridPaginatedVisibleSortedGridRowIdsSelector)(apiRef) : (0, _gridFilterSelector.gridExpandedSortedRowIdsSelector)(apiRef);
    apiRef.current.selectRows(rowsToBeSelected, params.value);
  }, [apiRef, props.checkboxSelectionVisibleOnly, props.pagination]);
  const handleCellKeyDown = React.useCallback((params, event) => {
    // Get the most recent cell mode because it may have been changed by another listener
    if (apiRef.current.getCellMode(params.id, params.field) === _gridEditRowModel.GridCellModes.Edit) {
      return;
    }

    // Ignore portal
    // Do not apply shortcuts if the focus is not on the cell root component
    if (!event.currentTarget.contains(event.target)) {
      return;
    }
    if ((0, _keyboardUtils.isNavigationKey)(event.key) && event.shiftKey) {
      // The cell that has focus after the keyboard navigation
      const focusCell = (0, _gridFocusStateSelector.gridFocusCellSelector)(apiRef);
      if (focusCell && focusCell.id !== params.id) {
        event.preventDefault();
        const isNextRowSelected = apiRef.current.isRowSelected(focusCell.id);
        if (!canHaveMultipleSelection) {
          apiRef.current.selectRow(focusCell.id, !isNextRowSelected, true);
          return;
        }
        const newRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(focusCell.id);
        const previousRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(params.id);
        let start;
        let end;
        if (newRowIndex > previousRowIndex) {
          if (isNextRowSelected) {
            // We are navigating to the bottom of the page and adding selected rows
            start = previousRowIndex;
            end = newRowIndex - 1;
          } else {
            // We are navigating to the bottom of the page and removing selected rows
            start = previousRowIndex;
            end = newRowIndex;
          }
        } else {
          // eslint-disable-next-line no-lonely-if
          if (isNextRowSelected) {
            // We are navigating to the top of the page and removing selected rows
            start = newRowIndex + 1;
            end = previousRowIndex;
          } else {
            // We are navigating to the top of the page and adding selected rows
            start = newRowIndex;
            end = previousRowIndex;
          }
        }
        const rowsBetweenStartAndEnd = visibleRows.rows.slice(start, end + 1).map(row => row.id);
        apiRef.current.selectRows(rowsBetweenStartAndEnd, !isNextRowSelected);
        return;
      }
    }
    if (event.key === ' ' && event.shiftKey) {
      event.preventDefault();
      handleSingleRowSelection(params.id, event);
      return;
    }
    if (event.key === 'a' && (event.ctrlKey || event.metaKey)) {
      event.preventDefault();
      selectRows(apiRef.current.getAllRowIds(), true);
    }
  }, [apiRef, handleSingleRowSelection, selectRows, visibleRows.rows, canHaveMultipleSelection]);
  (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'sortedRowsSet', runIfRowSelectionIsEnabled(removeOutdatedSelection));
  (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'rowClick', runIfRowSelectionIsEnabled(handleRowClick));
  (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'rowSelectionCheckboxChange', runIfRowSelectionIsEnabled(handleRowSelectionCheckboxChange));
  (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'headerSelectionCheckboxChange', handleHeaderSelectionCheckboxChange);
  (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'cellMouseDown', runIfRowSelectionIsEnabled(preventSelectionOnShift));
  (0, _useGridApiEventHandler.useGridApiEventHandler)(apiRef, 'cellKeyDown', runIfRowSelectionIsEnabled(handleCellKeyDown));

  /**
   * EFFECTS
   */
  React.useEffect(() => {
    if (propRowSelectionModel !== undefined) {
      apiRef.current.setRowSelectionModel(propRowSelectionModel);
    }
  }, [apiRef, propRowSelectionModel, props.rowSelection]);
  React.useEffect(() => {
    if (!props.rowSelection) {
      apiRef.current.setRowSelectionModel([]);
    }
  }, [apiRef, props.rowSelection]);
  const isStateControlled = propRowSelectionModel != null;
  React.useEffect(() => {
    if (isStateControlled || !props.rowSelection) {
      return;
    }

    // props.isRowSelectable changed
    const currentSelection = (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state);
    if (isRowSelectable) {
      const newSelection = currentSelection.filter(id => isRowSelectable(id));
      if (newSelection.length < currentSelection.length) {
        apiRef.current.setRowSelectionModel(newSelection);
      }
    }
  }, [apiRef, isRowSelectable, isStateControlled, props.rowSelection]);
  React.useEffect(() => {
    if (!props.rowSelection || isStateControlled) {
      return;
    }
    const currentSelection = (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state);
    if (!canHaveMultipleSelection && currentSelection.length > 1) {
      // See https://github.com/mui/mui-x/issues/8455
      apiRef.current.setRowSelectionModel([]);
    }
  }, [apiRef, canHaveMultipleSelection, checkboxSelection, isStateControlled, props.rowSelection]);
};
exports.useGridRowSelection = useGridRowSelection;