"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Popup = exports.popupContextTypes = void 0;
/* eslint-disable max-len */
var lodash_debounce_1 = __importDefault(require("lodash.debounce"));
var react_1 = __importDefault(require("react"));
var prop_types_1 = __importDefault(require("prop-types"));
var react_dom_1 = __importDefault(require("react-dom"));
var bem_react_classname_1 = require("bem-react-classname");
var cn_1 = require("../cn");
var resize_sensor_1 = require("../resize-sensor/resize-sensor");
var calc_drawing_params_1 = require("./calc-drawing-params");
var prop_types_2 = require("../lib/prop-types");
var window_1 = require("../lib/window");
var performance_1 = __importDefault(require("../performance"));
exports.popupContextTypes = {
    isInCustomContainer: prop_types_1.default.bool,
    renderContainerElement: prop_types_2.HtmlElement,
    positioningContainerElement: prop_types_2.HtmlElement,
};
/**
 * Компонент popup'а.
 */
var Popup = /** @class */ (function (_super) {
    __extends(Popup, _super);
    function Popup() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.cn = bem_react_classname_1.createCn('popup');
        _this.state = {
            direction: null,
            hasScrollbar: false,
            receivedContainer: false,
            styles: {
                top: 0,
                left: 0,
                height: 'auto',
            },
            topGradientStyles: {
                width: '100%',
            },
            bottomGradientStyles: {
                width: '100%',
            },
            canUseDOM: false,
            /*
             * Переменная для отложенного вызова функции redraw(),
             * которая будет вызвана после вызова componentDidMount().
             */
            needRedrawAfterMount: false,
        };
        _this.anchor = null;
        _this.clickEventBindTimeout = null;
        _this.domElemPopup = null;
        _this.domElemPopupInner = null;
        _this.domElemPopupContent = null;
        _this.isWindowClickBinded = false;
        _this.position = null;
        _this.handleWindowResize = lodash_debounce_1.default(function () {
            if (_this.isPropsToPositionCorrect()) {
                _this.redraw();
            }
        }, 200);
        _this.handleInnerScroll = function (event) {
            var _a = event.target, scrollTop = _a.scrollTop, offsetHeight = _a.offsetHeight, scrollHeight = _a.scrollHeight;
            var isTopReached = Math.round(scrollTop) === 0;
            var isBottomReached = Math.round(scrollTop) + offsetHeight === scrollHeight;
            if (_this.props.height === 'adaptive' || _this.props.target === 'screen') {
                var topGradientStyles = {
                    // eslint-disable-next-line react/no-access-state-in-setstate
                    width: _this.state.topGradientStyles.width,
                };
                var bottomGradientStyles = {
                    // eslint-disable-next-line react/no-access-state-in-setstate
                    width: _this.state.bottomGradientStyles.width,
                };
                if (isTopReached) {
                    topGradientStyles.height = 0;
                }
                if (isBottomReached) {
                    bottomGradientStyles.height = 0;
                }
                _this.setState({
                    topGradientStyles: topGradientStyles,
                    bottomGradientStyles: bottomGradientStyles,
                });
            }
        };
        _this.handleMouseEnter = function (event) {
            if (_this.props.onMouseEnter) {
                _this.props.onMouseEnter(event);
            }
        };
        _this.handleMouseLeave = function (event) {
            if (_this.props.onMouseLeave) {
                _this.props.onMouseLeave(event);
            }
        };
        _this.handleWindowClick = function (event) {
            if (_this.props.onClickOutside && !!_this.domElemPopup && window_1.isNodeOutsideElement(event.target, _this.domElemPopup)) {
                _this.props.onClickOutside(event);
            }
        };
        _this.handleResize = function () {
            if (!_this.props.visible) {
                return;
            }
            _this.redraw();
        };
        _this.redraw = function () {
            /*
             * Если функция redraw() была вызвана до componentDidMount,
             * то нужно отложить её вызов до момента,
             * когда this.state.canUseDOM будет равен значению true.
             *
             * Это сделано для того, чтобы redraw() не вызывалась на серверной стороне.
             */
            _this.setState({
                needRedrawAfterMount: true,
            });
            if (!_this.state.canUseDOM || !_this.isContainerReady()) {
                return;
            }
            _this.setState({
                needRedrawAfterMount: false,
            });
            if (!_this.isPropsToPositionCorrect()) {
                throw new Error('Cannot show popup without target or position');
            }
            if (!_this.domElemPopup) {
                _this.domElemPopup = _this.popup;
                _this.domElemPopupInner = _this.inner;
                _this.domElemPopupContent = _this.content;
            }
            var popupHash = _this.getPopupHash();
            var bestDrawingParams;
            switch (_this.props.target) {
                case 'position':
                    bestDrawingParams = { top: _this.position.top, left: _this.position.left };
                    break;
                case 'screen':
                    bestDrawingParams = {
                        top: 0,
                        left: 0,
                        right: 0,
                        bottom: 0,
                        overflow: _this.inner.scrollHeight > _this.inner.clientHeight,
                    };
                    break;
                case 'anchor':
                    bestDrawingParams = calc_drawing_params_1.calcBestDrawingParams(popupHash, calc_drawing_params_1.calcTargetDimensions(popupHash), calc_drawing_params_1.calcFitContainerDimensions(popupHash));
                    break;
            }
            _this.setState({
                direction: bestDrawingParams.direction,
                hasScrollbar: bestDrawingParams.overflow,
                styles: _this.getDrawingCss(bestDrawingParams),
            });
            _this.setGradientStyles();
        };
        return _this;
    }
    // eslint-disable-next-line camelcase
    Popup.prototype.UNSAFE_componentWillMount = function () {
        if (this.context.isInCustomContainer
            && this.context.renderContainerElement
            && this.context.positioningContainerElement) {
            this.setState({
                receivedContainer: true,
            });
        }
    };
    Popup.prototype.componentDidMount = function () {
        var _this = this;
        if (this.props.onClickOutside) {
            this.ensureClickEvent();
        }
        if (this.inner && (this.props.height === 'adaptive' || this.props.target === 'screen')) {
            this.setGradientStyles();
        }
        this.updateDocumentOverflow();
        window.addEventListener('resize', this.handleWindowResize);
        /* eslint-disable react/no-did-mount-set-state */
        this.setState({
            canUseDOM: true,
        }, function () {
            if (_this.state.needRedrawAfterMount) {
                _this.redraw();
            }
        });
        /* eslint-enable */
    };
    // eslint-disable-next-line camelcase
    Popup.prototype.UNSAFE_componentWillReceiveProps = function (nextProps, nextContext) {
        var _this = this;
        if (!this.state.receivedContainer
            && nextContext.renderContainerElement
            && nextContext.positioningContainerElement) {
            this.setState({
                receivedContainer: true,
            }, function () {
                if (_this.props.visible) {
                    _this.redraw();
                }
            });
            return;
        }
        if (nextProps.visible !== this.props.visible) {
            this.redraw();
        }
    };
    Popup.prototype.componentDidUpdate = function (prevProps) {
        if (this.props.onClickOutside) {
            if (prevProps.onClickOutside !== this.props.onClickOutside) {
                this.ensureClickEvent();
            }
            else if (prevProps.visible !== this.props.visible) {
                this.ensureClickEvent(!this.props.visible);
            }
        }
        this.updateDocumentOverflow();
    };
    Popup.prototype.componentWillUnmount = function () {
        if (this.props.onClickOutside) {
            this.ensureClickEvent(true);
        }
        this.updateDocumentOverflow(true);
        // Cancel debouncing to avoid `this.setState()` invocation in unmounted component state
        this.handleWindowResize.cancel();
        window.removeEventListener('resize', this.handleWindowResize);
    };
    Popup.prototype.render = function () {
        var _this = this;
        if (!this.state.canUseDOM || !this.isContainerReady()) {
            return null;
        }
        var template = (react_1.default.createElement("div", { ref: function (popup) {
                _this.popup = popup;
            }, "data-for": this.props.for, className: this.cn({
                direction: this.state.direction,
                type: this.props.target === 'anchor' && this.props.type === 'tooltip' && this.props.type,
                target: this.props.target,
                size: this.props.size,
                visible: this.props.visible,
                height: this.props.height,
                padded: this.props.padded,
                overflow: !!this.props.maxHeight,
            }), id: this.props.id, style: __assign(__assign({}, this.state.styles), { minWidth: this.getMinWidth(), maxWidth: this.getMaxWidth(), maxHeight: this.getMaxHeight() }), onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, "data-test-id": this.props['data-test-id'] },
            react_1.default.createElement("div", { className: this.cn('container') },
                this.props.header && react_1.default.createElement("div", { className: this.cn('header') }, this.props.header),
                react_1.default.createElement("div", { ref: function (inner) {
                        _this.inner = inner;
                    }, className: this.cn('inner'), onScroll: this.handleInnerScroll },
                    react_1.default.createElement("div", { className: this.cn('content'), ref: function (content) {
                            _this.content = content;
                        } },
                        this.props.children,
                        react_1.default.createElement(resize_sensor_1.ResizeSensor, { onResize: this.handleResize }))),
                this.state.hasScrollbar && (react_1.default.createElement("div", null,
                    react_1.default.createElement("div", { className: this.cn('gradient', { top: true }), style: this.state.topGradientStyles }),
                    react_1.default.createElement("div", { className: this.cn('gradient', { bottom: true }), style: this.state.bottomGradientStyles }))))));
        return react_dom_1.default.createPortal(template, this.getRenderContainer());
    };
    /**
     * Задает элемент, к которому будет привязан popup.
     * @param target Элемент, к которому будет привязан popup
     */
    Popup.prototype.setTarget = function (target) {
        if (this.anchor === target) {
            return;
        }
        this.anchor = target;
        this.redraw();
    };
    /**
     * Задает положение popup.
     * @param left x-coordinate
     * @param top y-coordinate
     */
    Popup.prototype.setPosition = function (left, top) {
        this.position = { left: left, top: top };
        this.redraw();
    };
    /**
     * Возвращает внутренний DOM узел.
     */
    Popup.prototype.getInnerNode = function () {
        return this.domElemPopupInner;
    };
    /**
     * Возвращает контейнер, в котором отрендерится попап.
     */
    Popup.prototype.getRenderContainer = function () {
        if (!this.context.isInCustomContainer) {
            return document.body;
        }
        return this.context.renderContainerElement;
    };
    /**
     * Возвращает контейнер, внутрь которого надо вписать элемент.
     */
    Popup.prototype.getPositioningContainer = function () {
        if (!this.context.isInCustomContainer) {
            return null;
        }
        return this.context.positioningContainerElement;
    };
    /**
     * Возвращает `true`, если контейнер, в котором должен быть отрисован
     * `Popup` уже находится в DOM. Для `Popup` без кастомного контейнера
     * роль контейнера выполняет `document.body` и этот для них этот метод
     * всегда вернете `true`.
     */
    Popup.prototype.isContainerReady = function () {
        if (!this.context.isInCustomContainer) {
            return true;
        }
        return this.context.isInCustomContainer && this.state.receivedContainer;
    };
    /**
     * Возвращает `true`, если все необходимые для расчета положения `Popup`
     * внешние props заданы.
     */
    Popup.prototype.isPropsToPositionCorrect = function () {
        return ((this.props.target === 'anchor' && this.anchor)
            || (this.props.target === 'position' && this.position)
            || this.props.target === 'screen');
    };
    Popup.prototype.ensureClickEvent = function (isDestroy) {
        var _this = this;
        var isNeedBindEvent = isDestroy === undefined ? this.props.visible : !isDestroy;
        // We need timeouts to not to catch the event that causes
        // popup opening (because it propagates to the `window`).
        if (this.clickEventBindTimeout) {
            clearTimeout(this.clickEventBindTimeout);
            this.clickEventBindTimeout = null;
        }
        this.clickEventBindTimeout = setTimeout(function () {
            if (!_this.isWindowClickBinded && isNeedBindEvent) {
                window.addEventListener('click', _this.handleWindowClick);
                window.addEventListener('touchend', _this.handleWindowClick);
                _this.isWindowClickBinded = true;
            }
            else if (_this.isWindowClickBinded && !isNeedBindEvent) {
                window.removeEventListener('click', _this.handleWindowClick);
                window.removeEventListener('touchend', _this.handleWindowClick);
                _this.isWindowClickBinded = false;
            }
        }, 0);
    };
    Popup.prototype.getDrawingCss = function (drawingParams) {
        return {
            top: drawingParams.top,
            left: drawingParams.left,
            right: drawingParams.right,
            bottom: drawingParams.bottom,
            height: this.props.height === 'adaptive' ? drawingParams.height : 'auto',
        };
    };
    Popup.prototype.getMinWidth = function () {
        return this.props.minWidth === undefined ? 0 : this.props.minWidth;
    };
    Popup.prototype.getMaxWidth = function () {
        return this.props.maxWidth === undefined ? 'none' : this.props.maxWidth;
    };
    Popup.prototype.getMaxHeight = function () {
        return this.props.maxHeight === undefined ? 'none' : this.props.maxHeight;
    };
    /**
     * Get collection of popup properties.
     *
     */
    Popup.prototype.getPopupHash = function () {
        return {
            directions: this.props.directions,
            bestDirection: this.state.direction,
            isHeightAdaptive: this.props.height === 'adaptive',
            isHeightAvailable: this.props.height === 'available',
            isTargetAnchor: this.props.target === 'anchor',
            isHaveTooltip: this.props.type === 'tooltip',
            width: this.domElemPopup.offsetWidth,
            height: this.domElemPopup.offsetHeight,
            contentWidth: this.domElemPopupContent.offsetWidth,
            contentHeight: this.domElemPopupContent.offsetHeight,
            offset: {
                main: this.props.mainOffset,
                second: this.props.secondaryOffset,
                fitContainer: this.props.fitContaiterOffset,
            },
            targetPosition: this.position,
            targetAnchor: this.anchor,
            fitContainer: this.getPositioningContainer(),
        };
    };
    Popup.prototype.setGradientStyles = function () {
        var clientWidth = this.inner.clientWidth;
        this.setState({
            topGradientStyles: {
                width: clientWidth,
                height: 0,
            },
            bottomGradientStyles: {
                width: clientWidth,
            },
        });
    };
    /**
     * Обновляет overflow у html. Нужно чтобы в полноэкранном режиме не скроллился документ
     * @param willComponentUnmount указывает на то, что функция вызывается при размонтировании компонента
     */
    Popup.prototype.updateDocumentOverflow = function (willComponentUnmount) {
        if (this.props.target !== 'screen') {
            return;
        }
        if (this.props.visible && !willComponentUnmount) {
            document.documentElement.style.overflow = 'hidden';
        }
        else {
            document.documentElement.style.overflow = '';
        }
    };
    Popup.defaultProps = {
        visible: false,
        padded: true,
        secondaryOffset: 0,
        fitContaiterOffset: 0,
        target: 'anchor',
        size: 's',
    };
    Popup.contextTypes = exports.popupContextTypes;
    Popup = __decorate([
        performance_1.default(true)
    ], Popup);
    return Popup;
}(react_1.default.Component));
exports.Popup = Popup;
exports.default = cn_1.withTheme(Popup);

//# sourceMappingURL=popup.js.map
