"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var React = require("react");
var PropTypes = require("prop-types");
var Utilities_1 = require("../../Utilities");
var getClassNames = Utilities_1.classNamesFunction();
var ScrollablePaneBase = /** @class */ (function (_super) {
    tslib_1.__extends(ScrollablePaneBase, _super);
    function ScrollablePaneBase(props) {
        var _this = _super.call(this, props) || this;
        _this._root = Utilities_1.createRef();
        _this._stickyAboveRef = Utilities_1.createRef();
        _this._stickyBelowRef = Utilities_1.createRef();
        _this._contentContainer = Utilities_1.createRef();
        _this.subscribe = function (handler) {
            _this._subscribers.add(handler);
        };
        _this.unsubscribe = function (handler) {
            _this._subscribers.delete(handler);
        };
        _this.addSticky = function (sticky) {
            _this._stickies.add(sticky);
            // If ScrollablePane is mounted, then sort sticky in correct place
            if (_this.contentContainer) {
                sticky.setDistanceFromTop(_this.contentContainer);
                _this.sortSticky(sticky);
            }
            _this.notifySubscribers();
        };
        _this.removeSticky = function (sticky) {
            _this._stickies.delete(sticky);
            _this._removeStickyFromContainers(sticky);
            _this.notifySubscribers();
        };
        _this.sortSticky = function (sticky) {
            if (_this.stickyAbove && _this.stickyBelow) {
                if (sticky.canStickyTop && sticky.stickyContentTop) {
                    _this._addToStickyContainer(sticky, _this.stickyAbove, sticky.stickyContentTop);
                }
                if (sticky.canStickyBottom && sticky.stickyContentBottom) {
                    _this._addToStickyContainer(sticky, _this.stickyBelow, sticky.stickyContentBottom);
                }
            }
        };
        _this.updateStickyRefHeights = function () {
            var stickyItems = _this._stickies;
            var stickyTopHeight = 0;
            var stickyBottomHeight = 0;
            stickyItems.forEach(function (sticky) {
                var _a = sticky.state, isStickyTop = _a.isStickyTop, isStickyBottom = _a.isStickyBottom;
                if (sticky.nonStickyContent) {
                    if (isStickyTop) {
                        stickyTopHeight += sticky.nonStickyContent.offsetHeight;
                    }
                    if (isStickyBottom) {
                        stickyBottomHeight += sticky.nonStickyContent.offsetHeight;
                    }
                    _this._checkStickyStatus(sticky);
                }
            });
            _this.setState({
                stickyTopHeight: stickyTopHeight,
                stickyBottomHeight: stickyBottomHeight
            });
        };
        _this.notifySubscribers = function () {
            if (_this.contentContainer) {
                _this._subscribers.forEach(function (handle) {
                    // this.stickyBelow is passed in for calculating distance to determine Sticky status
                    handle(_this.contentContainer, _this.stickyBelow);
                });
            }
        };
        _this.getScrollPosition = function () {
            if (_this.contentContainer) {
                return _this.contentContainer.scrollTop;
            }
            return 0;
        };
        _this._addToStickyContainer = function (sticky, stickyContainer, stickyContentToAdd) {
            // If there's no children, append child to list, otherwise, sort though array and append at correct position
            if (!stickyContainer.children.length) {
                stickyContainer.appendChild(stickyContentToAdd);
            }
            else {
                // If stickyContentToAdd isn't a child element of target container, then append
                if (!stickyContainer.contains(stickyContentToAdd)) {
                    var stickyChildrenElements_1 = [].slice.call(stickyContainer.children);
                    var stickyList_1 = [];
                    // Get stickies.  Filter by canStickyTop/Bottom, then sort by distance from top, and then
                    // filter by elements that are in the stickyContainer already.
                    _this._stickies.forEach(function (stickyItem) {
                        if (stickyContainer === _this.stickyAbove && sticky.canStickyTop) {
                            stickyList_1.push(stickyItem);
                        }
                        else if (sticky.canStickyBottom) {
                            stickyList_1.push(stickyItem);
                        }
                    });
                    var stickyListSorted = stickyList_1
                        .sort(function (a, b) {
                        return a.distanceFromTop - b.distanceFromTop;
                    })
                        .filter(function (item) {
                        var stickyContent = stickyContainer === _this.stickyAbove ? item.stickyContentTop : item.stickyContentBottom;
                        if (stickyContent) {
                            return stickyChildrenElements_1.indexOf(stickyContent) > -1;
                        }
                    });
                    // Get first element that has a distance from top that is further than our sticky that is being added
                    var targetStickyToAppendBefore = undefined;
                    for (var i in stickyListSorted) {
                        if (stickyListSorted[i].distanceFromTop >= sticky.distanceFromTop) {
                            targetStickyToAppendBefore = stickyListSorted[i];
                            break;
                        }
                    }
                    // If target element to append before is known, then grab respective stickyContentTop/Bottom element and insert before
                    var targetContainer = null;
                    if (targetStickyToAppendBefore) {
                        targetContainer =
                            stickyContainer === _this.stickyAbove
                                ? targetStickyToAppendBefore.stickyContentTop
                                : targetStickyToAppendBefore.stickyContentBottom;
                    }
                    stickyContainer.insertBefore(stickyContentToAdd, targetContainer);
                }
            }
        };
        _this._removeStickyFromContainers = function (sticky) {
            if (_this.stickyAbove && sticky.stickyContentTop) {
                _this.stickyAbove.removeChild(sticky.stickyContentTop);
            }
            if (_this.stickyBelow && sticky.stickyContentBottom) {
                _this.stickyBelow.removeChild(sticky.stickyContentBottom);
            }
        };
        _this._onWindowResize = function () {
            _this.notifySubscribers();
        };
        _this._getStickyContainerStyle = function (height) {
            return {
                height: height,
                width: _this.contentContainer ? _this.contentContainer.clientWidth : '100%'
            };
        };
        _this._subscribers = new Set();
        _this._stickies = new Set();
        _this.state = {
            stickyTopHeight: 0,
            stickyBottomHeight: 0
        };
        return _this;
    }
    Object.defineProperty(ScrollablePaneBase.prototype, "root", {
        get: function () {
            return this._root.current;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ScrollablePaneBase.prototype, "stickyAbove", {
        get: function () {
            return this._stickyAboveRef.current;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ScrollablePaneBase.prototype, "stickyBelow", {
        get: function () {
            return this._stickyBelowRef.current;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ScrollablePaneBase.prototype, "contentContainer", {
        get: function () {
            return this._contentContainer.current;
        },
        enumerable: true,
        configurable: true
    });
    ScrollablePaneBase.prototype.getChildContext = function () {
        return {
            scrollablePane: {
                subscribe: this.subscribe,
                unsubscribe: this.unsubscribe,
                addSticky: this.addSticky,
                removeSticky: this.removeSticky,
                updateStickyRefHeights: this.updateStickyRefHeights,
                sortSticky: this.sortSticky,
                notifySubscribers: this.notifySubscribers
            }
        };
    };
    ScrollablePaneBase.prototype.componentDidMount = function () {
        var _this = this;
        var initialScrollPosition = this.props.initialScrollPosition;
        this._events.on(this.contentContainer, 'scroll', this._async.throttle(this.notifySubscribers, 50));
        this._events.on(window, 'resize', this._onWindowResize);
        if (this.contentContainer && initialScrollPosition) {
            this.contentContainer.scrollTop = initialScrollPosition;
        }
        // Set sticky distances from top property, then sort in correct order and notify subscribers
        this.setStickiesDistanceFromTop();
        this._stickies.forEach(function (sticky) {
            _this.sortSticky(sticky);
        });
        this.notifySubscribers();
        if ('MutationObserver' in window) {
            this._mutationObserver = new MutationObserver(function (mutation) {
                // Function to check if mutation is occuring in stickyAbove or stickyBelow
                function checkIfMutationIsSticky(mutationRecord) {
                    if (this.stickyAbove !== null && this.stickyBelow !== null) {
                        return this.stickyAbove.contains(mutationRecord.target) || this.stickyBelow.contains(mutationRecord.target);
                    }
                    return false;
                }
                // If mutation occurs in sticky header or footer, then update sticky top/bottom heights
                if (mutation.some(checkIfMutationIsSticky.bind(_this))) {
                    _this.updateStickyRefHeights();
                }
                else {
                    // Notify subscribers again to re-check whether Sticky should be Sticky'd or not
                    _this.notifySubscribers();
                    // If mutation occurs in scrollable region, then find Sticky it belongs to and force update
                    var stickyList_2 = [];
                    _this._stickies.forEach(function (sticky) {
                        if (sticky.root && sticky.root.contains(mutation[0].target)) {
                            stickyList_2.push(sticky);
                        }
                    });
                    if (stickyList_2.length) {
                        stickyList_2.forEach(function (sticky) {
                            sticky.forceUpdate();
                        });
                    }
                }
            });
            if (this.root) {
                this._mutationObserver.observe(this.root, {
                    childList: true,
                    attributes: true,
                    subtree: true,
                    characterData: true
                });
            }
        }
    };
    ScrollablePaneBase.prototype.componentWillUnmount = function () {
        this._events.off(this.contentContainer);
        this._events.off(window);
        this._mutationObserver.disconnect();
    };
    // Only updates if props/state change, just to prevent excessive setState with updateStickyRefHeights
    ScrollablePaneBase.prototype.shouldComponentUpdate = function (nextProps, nextState) {
        return (this.props.children !== nextProps.children ||
            this.props.initialScrollPosition !== nextProps.initialScrollPosition ||
            this.props.className !== nextProps.className ||
            this.state.stickyTopHeight !== nextState.stickyTopHeight ||
            this.state.stickyBottomHeight !== nextState.stickyBottomHeight);
    };
    ScrollablePaneBase.prototype.componentDidUpdate = function (prevProps, prevState) {
        var initialScrollPosition = this.props.initialScrollPosition;
        if (this.contentContainer && initialScrollPosition && prevProps.initialScrollPosition !== initialScrollPosition) {
            this.contentContainer.scrollTop = initialScrollPosition;
        }
        // Update subscribers when stickyTopHeight/stickyBottomHeight changes
        if (prevState.stickyTopHeight !== this.state.stickyTopHeight ||
            prevState.stickyBottomHeight !== this.state.stickyBottomHeight) {
            this.notifySubscribers();
        }
    };
    ScrollablePaneBase.prototype.render = function () {
        var _a = this.props, className = _a.className, theme = _a.theme, styles = _a.styles;
        var _b = this.state, stickyTopHeight = _b.stickyTopHeight, stickyBottomHeight = _b.stickyBottomHeight;
        var classNames = getClassNames(styles, {
            theme: theme,
            className: className
        });
        return (React.createElement("div", tslib_1.__assign({}, Utilities_1.getNativeProps(this.props, Utilities_1.divProperties), { ref: this._root, className: classNames.root }),
            React.createElement("div", { ref: this._contentContainer, className: classNames.contentContainer, "data-is-scrollable": true }, this.props.children),
            React.createElement("div", { ref: this._stickyAboveRef, className: classNames.stickyAbove, style: this._getStickyContainerStyle(stickyTopHeight) }),
            React.createElement("div", { className: classNames.stickyBelow, style: this._getStickyContainerStyle(stickyBottomHeight) },
                React.createElement("div", { ref: this._stickyBelowRef, className: classNames.stickyBelowItems }))));
    };
    ScrollablePaneBase.prototype.setStickiesDistanceFromTop = function () {
        var _this = this;
        if (this.contentContainer) {
            this._stickies.forEach(function (sticky) {
                sticky.setDistanceFromTop(_this.contentContainer);
            });
        }
    };
    ScrollablePaneBase.prototype.forceLayoutUpdate = function () {
        this._onWindowResize();
    };
    ScrollablePaneBase.prototype._checkStickyStatus = function (sticky) {
        if (this.stickyAbove && this.stickyBelow && this.contentContainer && sticky.nonStickyContent) {
            // If sticky is sticky, then append content to appropriate container
            if (sticky.state.isStickyTop || sticky.state.isStickyBottom) {
                if (sticky.state.isStickyTop &&
                    !this.stickyAbove.contains(sticky.nonStickyContent) &&
                    sticky.stickyContentTop) {
                    sticky.addSticky(sticky.stickyContentTop);
                }
                if (sticky.state.isStickyBottom &&
                    !this.stickyBelow.contains(sticky.nonStickyContent) &&
                    sticky.stickyContentBottom) {
                    sticky.addSticky(sticky.stickyContentBottom);
                }
            }
            else if (!this.contentContainer.contains(sticky.nonStickyContent)) {
                // Reset sticky if it's not sticky and not in the contentContainer element
                sticky.resetSticky();
            }
        }
    };
    ScrollablePaneBase.childContextTypes = {
        scrollablePane: PropTypes.object
    };
    return ScrollablePaneBase;
}(Utilities_1.BaseComponent));
exports.ScrollablePaneBase = ScrollablePaneBase;
//# sourceMappingURL=ScrollablePane.base.js.map