UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

367 lines • 18 kB
import * as tslib_1 from "tslib"; import * as React from 'react'; import * as PropTypes from 'prop-types'; import { BaseComponent, classNamesFunction, divProperties, getNativeProps, createRef, getRTL } from '../../Utilities'; var getClassNames = classNamesFunction(); var ScrollablePaneBase = /** @class */ (function (_super) { tslib_1.__extends(ScrollablePaneBase, _super); function ScrollablePaneBase(props) { var _this = _super.call(this, props) || this; _this._root = createRef(); _this._stickyAboveRef = createRef(); _this._stickyBelowRef = createRef(); _this._contentContainer = 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 () { var scrollbarWidth = _this._getScrollbarWidth(); var scrollbarHeight = _this._getScrollbarHeight(); _this.setState({ scrollbarWidth: scrollbarWidth, scrollbarHeight: scrollbarHeight }); _this.notifySubscribers(); }; _this._getStickyContainerStyle = function (height, isTop) { return tslib_1.__assign({ height: height }, (getRTL() ? { right: '0', left: (_this.state.scrollbarWidth || _this._getScrollbarWidth() || 0) + "px" } : { left: '0', right: (_this.state.scrollbarWidth || _this._getScrollbarWidth() || 0) + "px" }), (isTop ? { top: '0' } : { bottom: (_this.state.scrollbarHeight || _this._getScrollbarHeight() || 0) + "px" })); }; _this._onScroll = function () { var contentContainer = _this.contentContainer; if (contentContainer) { _this._stickies.forEach(function (sticky) { sticky.syncScroll(contentContainer); }); } _this._notifyThrottled(); }; _this._subscribers = new Set(); _this._stickies = new Set(); _this.state = { stickyTopHeight: 0, stickyBottomHeight: 0, scrollbarWidth: undefined, scrollbarHeight: undefined }; _this._notifyThrottled = _this._async.throttle(_this.notifySubscribers, 50); 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._onScroll); 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; } // Notify subscribers again to re-check whether Sticky should be Sticky'd or not _this.notifySubscribers(); // If mutation occurs in sticky header or footer, then update sticky top/bottom heights if (mutation.some(checkIfMutationIsSticky.bind(_this))) { _this.updateStickyRefHeights(); } else { // 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 || this.state.scrollbarWidth !== nextState.scrollbarWidth || this.state.scrollbarHeight !== nextState.scrollbarHeight); }; ScrollablePaneBase.prototype.componentDidUpdate = function (prevProps, prevState) { var initialScrollPosition = this.props.initialScrollPosition; if (this.contentContainer && typeof initialScrollPosition === 'number' && 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(); } this._async.setTimeout(this._onWindowResize, 0); }; 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, scrollbarVisibility: this.props.scrollbarVisibility }); return (React.createElement("div", tslib_1.__assign({}, getNativeProps(this.props, 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, true) }), React.createElement("div", { className: classNames.stickyBelow, style: this._getStickyContainerStyle(stickyBottomHeight, false) }, 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.prototype._getScrollbarWidth = function () { var contentContainer = this.contentContainer; return contentContainer ? contentContainer.offsetWidth - contentContainer.clientWidth : undefined; }; ScrollablePaneBase.prototype._getScrollbarHeight = function () { var contentContainer = this.contentContainer; return contentContainer ? contentContainer.offsetHeight - contentContainer.clientHeight : undefined; }; ScrollablePaneBase.childContextTypes = { scrollablePane: PropTypes.object }; return ScrollablePaneBase; }(BaseComponent)); export { ScrollablePaneBase }; //# sourceMappingURL=ScrollablePane.base.js.map