import _extends from "@babel/runtime/helpers/esm/extends";
import _inheritsLoose from "@babel/runtime/helpers/esm/inheritsLoose";
var _class, _Loader;
import React from 'react';
import debounce from 'lodash.debounce';
import { globalObject, isBrowser } from '@skbkontur/global-object';
import * as LayoutEvents from "../../../lib/LayoutEvents";
import { Spinner } from "../../Spinner";
import { ThemeContext } from "../../../lib/theming/ThemeContext";
import { ZIndex } from "../../../internal/ZIndex";
import { CommonWrapper } from "../../../internal/CommonWrapper";
import { cx } from "../../../lib/theming/Emotion";
import { isTestEnv } from "../../../lib/currentEnvironment";
import { TaskWithDelayAndMinimalDuration } from "../../../lib/taskWithDelayAndMinimalDuration";
import { getTabbableElements } from "../../../lib/dom/tabbableHelpers";
import { rootNode } from "../../../lib/rootNode";
import { getDOMRect } from "../../../lib/dom/getDOMRect";
import { createPropsGetter } from "../../../lib/createPropsGetter";
import { styles } from "../Loader.styles";
var types = ['mini', 'normal', 'big'];
export var LoaderDataTids = {
  idle: 'Loader__Idle',
  veil: 'Loader__Veil',
  spinner: 'Loader__Spinner'
};

/**
 * Компонент `Loader` принимает внутрь себя контент, поверх которого в активном состоянии `Loader`'а будет отрисован спиннер.
 *
 * Если вам нужен только сам спиннер без дополнительного функционала - используйте компонент Spinner.
 */
export var Loader = rootNode(_class = (_Loader = /*#__PURE__*/function (_React$Component) {
  function Loader(props) {
    var _this;
    _this = _React$Component.call(this, props) || this;
    _this.getProps = createPropsGetter(Loader.defaultProps);
    _this.childrenRef = function (element) {
      _this.childrenContainerNode = element;
    };
    _this.spinnerRef = function (element) {
      _this.spinnerContainerNode = element;
    };
    _this.checkSpinnerPosition = function () {
      if (!_this.spinnerContainerNode || !isBrowser(globalObject)) {
        return;
      }
      var _getDOMRect = getDOMRect(_this.spinnerContainerNode),
        containerTop = _getDOMRect.top,
        containerRight = _getDOMRect.right,
        containerBottom = _getDOMRect.bottom,
        containerLeft = _getDOMRect.left,
        containerHeight = _getDOMRect.height,
        containerWidth = _getDOMRect.width;
      var windowHeight = globalObject.innerHeight;
      var windowWidth = globalObject.innerWidth;

      // Если контейнер не больше высоты и не шире окна,
      // то просто выравниваем по центру
      if (windowHeight >= containerHeight && windowWidth >= containerWidth) {
        _this.setState({
          isStickySpinner: false,
          spinnerStyle: {}
        });
        return;
      }
      var spinnerStyle = {
        top: 30,
        right: 0,
        bottom: 30,
        left: 0
      };

      // ПО ВЕРТИКАЛИ
      // Если верхний край контейнера ниже верхнего края окна,
      // то сдвигаем и лоадер
      if (containerTop > 0) {
        spinnerStyle.top = containerTop + 30;
      }

      // Если нижний край контейнера выше нижнего края окна,
      // то сдвигаем и лоадер
      if (containerBottom < windowHeight) {
        spinnerStyle.bottom = Math.abs(windowHeight - containerBottom) + 30;
      }

      // Если знаем высоту спиннера и нижний край контейнера поднимается
      // выше отступа на высоту спиннера, то убираем верхнюю позицию лоадера

      var spinnerHeight = getDOMRect(_this.spinnerNode).height;
      if (spinnerHeight && spinnerStyle.bottom >= windowHeight - spinnerHeight) {
        delete spinnerStyle.top;
      }

      // ПО ГОРИЗОНТАЛИ
      // Если левый край контейнера правее левого края окна,
      // то сдвигаем и лоадер
      if (containerLeft > 0) {
        spinnerStyle.left = containerLeft;
      }

      // Если правый край контейнера левее правого края окна,
      // то сдвигаем и лоадер
      if (containerRight < windowWidth) {
        spinnerStyle.right = windowWidth - containerRight;
      }
      _this.setState({
        isStickySpinner: true,
        spinnerStyle: spinnerStyle
      });
    };
    _this.disableChildrenFocus = function () {
      if (!_this.childrenObserver) {
        _this.makeObservable();
      }
      var tabbableElements = getTabbableElements(_this.childrenContainerNode);
      tabbableElements.forEach(function (el) {
        if (!el.hasAttribute('origin-tabindex')) {
          el.setAttribute('origin-tabindex', el.tabIndex.toString());
        }
        el.tabIndex = -1;
      });
    };
    _this.enableChildrenFocus = function () {
      var _globalObject$documen, _globalObject$documen2;
      _this.makeUnobservable();
      // NOTE: NodeList doesn't support 'forEach' method in IE11 and other older browsers
      Array.from((_globalObject$documen = (_globalObject$documen2 = globalObject.document) == null ? void 0 : _globalObject$documen2.querySelectorAll('[origin-tabindex]')) != null ? _globalObject$documen : []).forEach(function (el) {
        var _el$getAttribute;
        el.setAttribute('tabindex', (_el$getAttribute = el.getAttribute('origin-tabindex')) != null ? _el$getAttribute : '0');
        el.removeAttribute('origin-tabindex');
      });
    };
    _this.makeObservable = function () {
      var target = _this.childrenContainerNode;
      if (!target || !globalObject.MutationObserver) {
        return;
      }
      var config = {
        childList: true,
        subtree: true
      };
      var observer = new globalObject.MutationObserver(_this.disableChildrenFocus);
      observer.observe(target, config);
      _this.childrenObserver = observer;
    };
    _this.makeUnobservable = function () {
      var _this$childrenObserve;
      (_this$childrenObserve = _this.childrenObserver) == null || _this$childrenObserve.disconnect();
      _this.childrenObserver = null;
    };
    _this.spinnerContainerNode = null;
    _this.childrenContainerNode = null;
    _this.childrenObserver = null;
    _this.spinnerNode = null;
    _this.state = {
      isStickySpinner: false,
      isSpinnerVisible: false,
      isLoaderActive: false
    };
    _this.spinnerTask = new TaskWithDelayAndMinimalDuration({
      delayBeforeTaskStart: _this.getProps().delayBeforeSpinnerShow,
      durationOfTask: _this.getProps().minimalDelayBeforeSpinnerHide,
      taskStartCallback: function taskStartCallback() {
        return _this.setState({
          isSpinnerVisible: true
        });
      },
      taskStopCallback: function taskStopCallback() {
        return _this.setState({
          isSpinnerVisible: false
        });
      }
    });
    return _this;
  }
  _inheritsLoose(Loader, _React$Component);
  var _proto = Loader.prototype;
  _proto.componentDidMount = function componentDidMount() {
    var active = this.getProps().active;
    this.checkSpinnerPosition();
    active && this.spinnerTask.start();
    this.layoutEvents = LayoutEvents.addListener(debounce(this.checkSpinnerPosition, 10));
    if (active) {
      this.disableChildrenFocus();
    }
  };
  _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
    var component = this.props.component;
    var _this$getProps = this.getProps(),
      active = _this$getProps.active,
      delayBeforeSpinnerShow = _this$getProps.delayBeforeSpinnerShow,
      minimalDelayBeforeSpinnerHide = _this$getProps.minimalDelayBeforeSpinnerHide;
    var isLoaderActive = this.state.isLoaderActive;
    if (active && !prevProps.active || prevProps.component !== component) {
      this.checkSpinnerPosition();
    }
    if (delayBeforeSpinnerShow !== prevProps.delayBeforeSpinnerShow || minimalDelayBeforeSpinnerHide !== prevProps.minimalDelayBeforeSpinnerHide) {
      this.spinnerTask.update({
        delayBeforeTaskStart: delayBeforeSpinnerShow,
        durationOfTask: minimalDelayBeforeSpinnerHide
      });
    }
    if (active !== prevProps.active) {
      active ? this.spinnerTask.start() : this.spinnerTask.stop();
    }
    if (isLoaderActive !== prevState.isLoaderActive) {
      if (isLoaderActive) {
        this.disableChildrenFocus();
      } else {
        this.enableChildrenFocus();
      }
    }
  };
  _proto.componentWillUnmount = function componentWillUnmount() {
    this.makeUnobservable();
    if (this.layoutEvents) {
      this.layoutEvents.remove();
    }
    this.spinnerTask.clearTask();
  };
  Loader.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) {
    if (props.active && !state.isLoaderActive) {
      return {
        isLoaderActive: true
      };
    }
    if (state.isLoaderActive && !(props.active || state.isSpinnerVisible)) {
      return {
        isLoaderActive: false
      };
    }
    return state;
  };
  _proto.render = function render() {
    var _this2 = this;
    return /*#__PURE__*/React.createElement(ThemeContext.Consumer, null, function (theme) {
      _this2.theme = theme;
      return _this2.renderMain();
    });
  };
  _proto.renderMain = function renderMain() {
    var _cx;
    var _this$props = this.props,
      caption = _this$props.caption,
      component = _this$props.component;
    var type = this.getProps().type;
    var isLoaderActive = this.state.isLoaderActive;
    return /*#__PURE__*/React.createElement(CommonWrapper, _extends({
      rootNodeRef: this.setRootNode
    }, this.props), /*#__PURE__*/React.createElement("div", {
      className: styles.loader(),
      "data-tid": this.props['data-tid'] || (isLoaderActive ? LoaderDataTids.veil : LoaderDataTids.idle)
    }, /*#__PURE__*/React.createElement(ZIndex, {
      applyZIndex: isLoaderActive,
      coverChildren: isLoaderActive,
      style: {
        height: '100%'
      },
      wrapperRef: this.childrenRef
    }, this.props.children), isLoaderActive && /*#__PURE__*/React.createElement(ZIndex, {
      wrapperRef: this.spinnerRef,
      className: cx((_cx = {}, _cx[styles.active(this.theme)] = isLoaderActive, _cx))
    }, this.state.isSpinnerVisible && this.renderSpinner(type, caption, component))));
  };
  _proto.renderSpinner = function renderSpinner(type, caption, component) {
    var _cx2,
      _this3 = this;
    return /*#__PURE__*/React.createElement("span", {
      "data-tid": LoaderDataTids.spinner,
      className: cx(styles.spinnerContainer(), (_cx2 = {}, _cx2[styles.spinnerContainerSticky()] = this.state.isStickySpinner, _cx2)),
      style: this.state.spinnerStyle
    }, /*#__PURE__*/React.createElement("div", {
      className: styles.spinnerComponentWrapper(),
      ref: function ref(element) {
        _this3.spinnerNode = element;
      }
    }, component !== undefined ? component : /*#__PURE__*/React.createElement(Spinner, {
      type: type,
      caption: caption
    })));
  };
  return Loader;
}(React.Component), _Loader.__KONTUR_REACT_UI__ = 'Loader', _Loader.displayName = 'Loader', _Loader.defaultProps = {
  type: 'normal',
  active: false,
  delayBeforeSpinnerShow: isTestEnv ? 0 : 300,
  minimalDelayBeforeSpinnerHide: isTestEnv ? 0 : 1000
}, _Loader)) || _class;