import _extends from '@babel/runtime/helpers/extends';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
import _pick from '../../node_modules/lodash-es/pick.js';
import _find from '../../node_modules/lodash-es/find.js';
import 'core-js/modules/es.symbol.js';
import 'core-js/modules/es.array.filter.js';
import 'core-js/modules/es.array.find.js';
import 'core-js/modules/es.array.map.js';
import 'core-js/modules/es.array.sort.js';
import 'core-js/modules/es.function.name.js';
import 'core-js/modules/es.object.entries.js';
import 'core-js/modules/es.object.get-own-property-descriptor.js';
import 'core-js/modules/es.object.get-own-property-descriptors.js';
import 'core-js/modules/es.object.keys.js';
import 'core-js/modules/es.object.to-string.js';
import 'core-js/modules/web.dom-collections.for-each.js';
import React__default, { useRef, useMemo, useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { WidthProvider, Responsive } from 'react-grid-layout';
import classnames from 'classnames';
import { useResize } from '../../internal/UseResizeObserver.js';
import { settings } from '../../constants/Settings.js';
import { getLayout } from '../../utils/componentUtilityFunctions.js';
import { GUTTER, DASHBOARD_BREAKPOINTS, CARD_DIMENSIONS, DASHBOARD_COLUMNS, ROW_HEIGHT, DASHBOARD_CONTAINER_PADDING, CARD_SIZES } from '../../constants/LayoutConstants.js';
import { DashboardLayoutPropTypes } from '../../constants/CardPropTypes.js';

var _excluded = ["breakpointSizes"],
  _excluded2 = ["children", "breakpoint", "isEditable", "layouts", "supportedLayouts", "onLayoutChange", "onBreakpointChange", "onCardSizeChange", "onResizeStop", "testId", "useCSSTransforms"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var iotPrefix = settings.iotPrefix;
var GridLayout = WidthProvider(Responsive);
var DashboardGridPropTypes = {
  /** Array of elements to render in the grid (recommended that you use our Card component) */
  children: PropTypes.arrayOf(PropTypes.node).isRequired,
  /** current screen breakpoint which determine which dashboard layout to use */
  breakpoint: PropTypes.oneOf(['max', 'xl', 'lg', 'md', 'sm', 'xs']),
  /** Is the dashboard currently in the editable state */
  isEditable: PropTypes.bool,
  /** current set of react-grid-layout rules for laying the cards out
   * Layout is an array of objects with the format:
   * {x: number, y: number, w: number, h: number}
   * The index into the layout must match the key used on each item component.
   * If you choose to use custom keys, you can specify that key in the layout
   * array objects like so:
   * {i: string, x: number, y: number, w: number, h: number}
   */
  layouts: PropTypes.shape({
    max: PropTypes.arrayOf(DashboardLayoutPropTypes),
    xl: PropTypes.arrayOf(DashboardLayoutPropTypes),
    lg: PropTypes.arrayOf(DashboardLayoutPropTypes),
    md: PropTypes.arrayOf(DashboardLayoutPropTypes),
    sm: PropTypes.arrayOf(DashboardLayoutPropTypes),
    xs: PropTypes.arrayOf(DashboardLayoutPropTypes)
  }),
  /** Array of layouts that are supported by this component. Defaults to all layouts */
  supportedLayouts: PropTypes.arrayOf(PropTypes.oneOf(['max', 'xl', 'lg', 'md', 'sm', 'xs'])),
  /**
   * Optionally listen to layout changes to update a dashboard template
   * Calls back with (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,
   */
  onLayoutChange: PropTypes.func,
  /** Optionally listen to window resize events to update a dashboard template */
  onBreakpointChange: PropTypes.func,
  /** Callback for when a card has been resized */
  onResizeStop: PropTypes.func,
  /** Callback for when a card has been resized by drag */
  onCardSizeChange: PropTypes.func,
  testId: PropTypes.string,
  useCSSTransforms: PropTypes.bool
};
var defaultProps = {
  breakpoint: 'lg',
  isEditable: false,
  layouts: {},
  supportedLayouts: Object.keys(DASHBOARD_BREAKPOINTS),
  onLayoutChange: null,
  onBreakpointChange: null,
  onResizeStop: null,
  onCardSizeChange: null,
  testId: 'dashboard-grid',
  useCSSTransforms: true
};
var getClosestMatchingSizes = function getClosestMatchingSizes(_ref) {
  var sortedSizes = _ref.sortedSizes,
    value = _ref.value,
    dimension = _ref.dimension;
  var closestLargerSize = sortedSizes.find(function (size) {
    return size[dimension] > value;
  });
  var closestDimensionValue = closestLargerSize ? closestLargerSize[dimension] : sortedSizes[sortedSizes.length - 1][dimension];
  return sortedSizes.filter(function (size) {
    return size[dimension] === closestDimensionValue;
  });
};
var getMatchingCardSizesByDimension = function getMatchingCardSizesByDimension(_ref2) {
  var breakpointSizes = _ref2.breakpointSizes,
    rest = _objectWithoutProperties(_ref2, _excluded);
  var value = rest.value,
    dimension = rest.dimension;
  var sortedSizes = breakpointSizes.sort(function (a, b) {
    return a[dimension] - b[dimension];
  });
  var matchingSizes = sortedSizes.filter(function (size) {
    return size[dimension] === value;
  });
  return matchingSizes.length ? matchingSizes : getClosestMatchingSizes(_objectSpread({
    sortedSizes: sortedSizes
  }, rest));
};

/**
 * Returns the closest larger matching card size (SMALL, MEDIUM etc) based on the
 * dimensions (height first) of a layoutItems.
 * @param {*} layoutItem a layoutItem with modified dimensions
 * @param {*} breakpointSizes list of card size objects for a specific breakpoint
 */
var getMatchingCardSize = function getMatchingCardSize(layoutItem, breakpointSizes) {
  var sizesMatchingHeight = getMatchingCardSizesByDimension({
    breakpointSizes: breakpointSizes,
    value: layoutItem.h,
    dimension: 'h'
  });
  return getMatchingCardSizesByDimension({
    breakpointSizes: sizesMatchingHeight,
    value: layoutItem.w,
    dimension: 'w'
  })[0];
};

/**
 * Used to generate a list card size objects for a specific breakpoint.
 * The list objects have the props 'h', 'w' and 'name' where name is the card size name.
 * @param {*} breakpoint
 * @param {*} cardDimensions see CARD_DIMENSIONS
 * @param {*} cardSizes see CARD_SIZES
 */
var getBreakPointSizes = function getBreakPointSizes(breakpoint, cardDimensions, cardSizes) {
  return Object.entries(cardDimensions).map(function (_ref3) {
    var _ref4 = _slicedToArray(_ref3, 2),
      name = _ref4[0],
      breakpoints = _ref4[1];
    return _objectSpread(_objectSpread({}, breakpoints[breakpoint]), {}, {
      name: name
    });
  })
  // Filter out legacy sizes
  .filter(function (entry) {
    return cardSizes[entry.name];
  });
};

/**
 * This function finds an existing layout for each dashboard breakpoint, validates it, and or generates a new one to return
 * @param {Object} layouts a keyed object of each layout for each breakpoint
 * @param {Array<Object>} cards an array of the card props for each card
 * @param {Array<string>} supportedLayouts
 */
var findLayoutOrGenerate = function findLayoutOrGenerate(layouts, cards, supportedLayouts) {
  // iterate through each breakpoint
  return supportedLayouts.reduce(function (acc, layoutName) {
    var layout = layouts && layouts[layoutName];
    // If layouts exists and there is one for each card
    if (layout && layout.length === cards.length) {
      // We need to set the width and height based on the card.size using CARD_DIMENSIONS
      layout = layout.map(function (cardLayout) {
        var matchingCard = _find(cards, {
          id: cardLayout.i
        });
        return _objectSpread(_objectSpread({}, cardLayout), matchingCard ? _objectSpread({}, CARD_DIMENSIONS[matchingCard.size][layoutName]) : {});
      });
    } else {
      // generate the layout if we're not passed one from the parent or if the layouts are missing for any breakpoints
      layout = getLayout(layoutName, cards, DASHBOARD_COLUMNS, CARD_DIMENSIONS, layout);
    }

    // Add the resizable flag
    var layoutWithResizableItems = layout.map(function (cardFromLayout) {
      var matchingCard = _find(cards, {
        id: cardFromLayout.i
      });
      return _objectSpread(_objectSpread({}, cardFromLayout), {}, {
        isResizable: matchingCard === null || matchingCard === void 0 ? void 0 : matchingCard.isResizable
      });
    });
    return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, layoutName, layoutWithResizableItems));
  }, {});
};
var formatResizeResponse = function formatResizeResponse(params) {
  return {
    layout: params[0],
    oldItem: params[1],
    newItem: params[2],
    placeholder: params[3],
    event: params[4],
    element: params[5]
  };
};

/**
 * Renders the grid of cards according to the standardized PAL patterns for IoT.
 *
 * This is a stateless component but it does have some caching to help optimize performance.
 *
 * Gutter size and card dimensions, row heights, dashboard breakpoints, and column grids are standardized across IoT,
 * it passes this information down to each cards as it renders them.
 *
 * If the dashboardgrid is set to editable mode, it supports dragging and dropping the cards around.
 * On each change to card position, the onLayoutChange callback will be triggered.
 *
 * If the window is resized, the onBreakpointChange event will be fired.
 *
 * You can also pass any of the additional properties documented here:
 * https://github.com/STRML/react-grid-layout#grid-layout-props
 */
var DashboardGrid = function DashboardGrid(_ref5) {
  var _gridRef$current;
  var children = _ref5.children,
    breakpoint = _ref5.breakpoint,
    isEditable = _ref5.isEditable,
    layouts = _ref5.layouts,
    supportedLayouts = _ref5.supportedLayouts,
    onLayoutChange = _ref5.onLayoutChange,
    onBreakpointChange = _ref5.onBreakpointChange,
    onCardSizeChange = _ref5.onCardSizeChange,
    onResizeStopCallback = _ref5.onResizeStop,
    testId = _ref5.testId,
    useCSSTransforms = _ref5.useCSSTransforms,
    others = _objectWithoutProperties(_ref5, _excluded2);
  var gridRef = useResize(useRef(null));
  // Unfortunately can't use React.Children.map because it breaks the original key which breaks react-grid-layout
  var childrenArray = useMemo(function () {
    return children;
  }, [children]);
  var generatedLayouts = useMemo(function () {
    return findLayoutOrGenerate(layouts, childrenArray.map(function (card) {
      return card.props;
    }), supportedLayouts);
  }, [childrenArray, layouts, supportedLayouts]);
  var cachedMargin = useMemo(function () {
    return [GUTTER, GUTTER];
  }, []);
  var handleLayoutChange = useCallback(function (layout, allLayouts) {
    if (onLayoutChange) {
      onLayoutChange(layout, allLayouts);
    }
  }, [onLayoutChange]);

  // listen for the width to be adjusted and then dispatch the resize event
  // Work around for a bug in React-Grid-Layout. Issue here: https://github.com/STRML/react-grid-layout/issues/1204
  useEffect(function () {
    window.dispatchEvent(new Event('resize'));

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [gridRef === null || gridRef === void 0 || (_gridRef$current = gridRef.current) === null || _gridRef$current === void 0 ? void 0 : _gridRef$current.clientWidth]);

  // add the common measurements and key to the card so that the grid layout can find it
  var cards = useMemo(function () {
    return childrenArray.map(function (card) {
      return /*#__PURE__*/React__default.cloneElement(card, {
        key: card.props.id,
        dashboardBreakpoints: DASHBOARD_BREAKPOINTS,
        cardDimensions: CARD_DIMENSIONS,
        dashboardColumns: DASHBOARD_COLUMNS,
        rowHeight: ROW_HEIGHT
      });
    });
  }, [childrenArray]);
  var _useState = useState(false),
    _useState2 = _slicedToArray(_useState, 2),
    shouldAnimate = _useState2[0],
    setShouldAnimate = _useState2[1];
  useEffect(function () {
    var animationFramePromise = requestAnimationFrame(function () {
      setShouldAnimate(isEditable);
    });
    return function () {
      return animationFramePromise && cancelAnimationFrame(animationFramePromise);
    };
  }, [isEditable]);
  var breakpointSizes = useMemo(function () {
    return getBreakPointSizes(breakpoint, CARD_DIMENSIONS, CARD_SIZES);
  }, [breakpoint]);
  var onCardResize = function onCardResize() {
    for (var _len = arguments.length, params = new Array(_len), _key = 0; _key < _len; _key++) {
      params[_key] = arguments[_key];
    }
    var oldLayoutItem = params[1],
      layoutItem = params[2],
      placeholder = params[3];
    var rowsJumped = layoutItem.y - oldLayoutItem.y;
    var colsJumped = layoutItem.x - oldLayoutItem.x;
    var jumpAdjustedlayoutItem = _objectSpread(_objectSpread({}, layoutItem), {}, {
      h: layoutItem.h + rowsJumped,
      w: layoutItem.w + colsJumped
    });
    var matchedSize = getMatchingCardSize(jumpAdjustedlayoutItem, breakpointSizes);
    var renderedCardSizeName = cards.find(function (card) {
      return card.props.id === layoutItem.i;
    }).props.size;
    placeholder.h = matchedSize.h;
    placeholder.w = matchedSize.w;
    layoutItem.h = matchedSize.h;
    layoutItem.w = matchedSize.w;
    if (renderedCardSizeName !== matchedSize.name && onCardSizeChange) {
      var gridResponse = formatResizeResponse(params);
      onCardSizeChange({
        id: layoutItem.i,
        size: matchedSize.name
      }, gridResponse);
    }
  };
  var onResizeStop = function onResizeStop() {
    for (var _len2 = arguments.length, params = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
      params[_key2] = arguments[_key2];
    }
    var oldLayoutItem = params[1],
      layoutItem = params[2];
    var newSize = getMatchingCardSize(layoutItem, breakpointSizes);
    var oldSize = getMatchingCardSize(oldLayoutItem, breakpointSizes);
    /* istanbul ignore else */
    if (newSize !== oldSize) {
      layoutItem.h = newSize.h;
      layoutItem.w = newSize.w;
      /* istanbul ignore else */
      if (onResizeStopCallback) {
        var gridResponse = formatResizeResponse(params);
        onResizeStopCallback({
          id: layoutItem.i,
          size: newSize.name
        }, gridResponse);
      }
    }
  };
  return /*#__PURE__*/React__default.createElement("div", {
    "data-testid": testId,
    style: {
      flex: 1
    },
    ref: gridRef
  }, /*#__PURE__*/React__default.createElement(GridLayout, _extends({
    className: classnames("".concat(iotPrefix, "--dashboard-grid"), _defineProperty({}, "".concat(iotPrefix, "--dashboard-grid__animate"), shouldAnimate)),
    key: shouldAnimate // forced to throw away and regen the GridLayout as it doesn't update when className is updated for shouldAnimate
    ,
    layouts: generatedLayouts,
    cols: DASHBOARD_COLUMNS,
    breakpoints: _pick(DASHBOARD_BREAKPOINTS, supportedLayouts),
    margin: cachedMargin,
    containerPadding: DASHBOARD_CONTAINER_PADDING,
    rowHeight: ROW_HEIGHT[breakpoint],
    onLayoutChange: handleLayoutChange,
    onBreakpointChange: onBreakpointChange,
    onResize: onCardResize,
    onResizeStop: onResizeStop,
    isResizable: false,
    isDraggable: isEditable,
    useCSSTransforms: useCSSTransforms
  }, others), cards));
};
DashboardGrid.propTypes = DashboardGridPropTypes;
DashboardGrid.defaultProps = defaultProps;
DashboardGrid.__docgenInfo = {
  "description": "Renders the grid of cards according to the standardized PAL patterns for IoT.\n\nThis is a stateless component but it does have some caching to help optimize performance.\n\nGutter size and card dimensions, row heights, dashboard breakpoints, and column grids are standardized across IoT,\nit passes this information down to each cards as it renders them.\n\nIf the dashboardgrid is set to editable mode, it supports dragging and dropping the cards around.\nOn each change to card position, the onLayoutChange callback will be triggered.\n\nIf the window is resized, the onBreakpointChange event will be fired.\n\nYou can also pass any of the additional properties documented here:\nhttps://github.com/STRML/react-grid-layout#grid-layout-props",
  "methods": [],
  "displayName": "DashboardGrid",
  "props": {
    "breakpoint": {
      "defaultValue": {
        "value": "'lg'",
        "computed": false
      },
      "description": "current screen breakpoint which determine which dashboard layout to use",
      "type": {
        "name": "enum",
        "value": [{
          "value": "'max'",
          "computed": false
        }, {
          "value": "'xl'",
          "computed": false
        }, {
          "value": "'lg'",
          "computed": false
        }, {
          "value": "'md'",
          "computed": false
        }, {
          "value": "'sm'",
          "computed": false
        }, {
          "value": "'xs'",
          "computed": false
        }]
      },
      "required": false
    },
    "isEditable": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "Is the dashboard currently in the editable state",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "layouts": {
      "defaultValue": {
        "value": "{}",
        "computed": false
      },
      "description": "current set of react-grid-layout rules for laying the cards out\nLayout is an array of objects with the format:\n{x: number, y: number, w: number, h: number}\nThe index into the layout must match the key used on each item component.\nIf you choose to use custom keys, you can specify that key in the layout\narray objects like so:\n{i: string, x: number, y: number, w: number, h: number}",
      "type": {
        "name": "shape",
        "value": {
          "max": {
            "name": "arrayOf",
            "value": {
              "name": "custom",
              "raw": "DashboardLayoutPropTypes"
            },
            "required": false
          },
          "xl": {
            "name": "arrayOf",
            "value": {
              "name": "custom",
              "raw": "DashboardLayoutPropTypes"
            },
            "required": false
          },
          "lg": {
            "name": "arrayOf",
            "value": {
              "name": "custom",
              "raw": "DashboardLayoutPropTypes"
            },
            "required": false
          },
          "md": {
            "name": "arrayOf",
            "value": {
              "name": "custom",
              "raw": "DashboardLayoutPropTypes"
            },
            "required": false
          },
          "sm": {
            "name": "arrayOf",
            "value": {
              "name": "custom",
              "raw": "DashboardLayoutPropTypes"
            },
            "required": false
          },
          "xs": {
            "name": "arrayOf",
            "value": {
              "name": "custom",
              "raw": "DashboardLayoutPropTypes"
            },
            "required": false
          }
        }
      },
      "required": false
    },
    "supportedLayouts": {
      "defaultValue": {
        "value": "Object.keys(DASHBOARD_BREAKPOINTS)",
        "computed": true
      },
      "description": "Array of layouts that are supported by this component. Defaults to all layouts",
      "type": {
        "name": "arrayOf",
        "value": {
          "name": "enum",
          "value": [{
            "value": "'max'",
            "computed": false
          }, {
            "value": "'xl'",
            "computed": false
          }, {
            "value": "'lg'",
            "computed": false
          }, {
            "value": "'md'",
            "computed": false
          }, {
            "value": "'sm'",
            "computed": false
          }, {
            "value": "'xs'",
            "computed": false
          }]
        }
      },
      "required": false
    },
    "onLayoutChange": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Optionally listen to layout changes to update a dashboard template\nCalls back with (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "onBreakpointChange": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Optionally listen to window resize events to update a dashboard template",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "onResizeStop": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Callback for when a card has been resized",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "onCardSizeChange": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Callback for when a card has been resized by drag",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "testId": {
      "defaultValue": {
        "value": "'dashboard-grid'",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "useCSSTransforms": {
      "defaultValue": {
        "value": "true",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "children": {
      "description": "Array of elements to render in the grid (recommended that you use our Card component)",
      "type": {
        "name": "arrayOf",
        "value": {
          "name": "node"
        }
      },
      "required": true
    }
  }
};
var DashboardGrid$1 = DashboardGrid;

export { DashboardGridPropTypes, DashboardGrid$1 as default, findLayoutOrGenerate, getBreakPointSizes, getMatchingCardSize };
