UNPKG

@uifabric/experiments

Version:

Experimental React components for building experiences for Office 365.

158 lines 8.03 kB
define(["require", "exports", "tslib", "react", "../../utilities/scrolling/ScrollContainer", "office-ui-fabric-react/lib/Utilities"], function (require, exports, tslib_1, React, ScrollContainer_1, Utilities_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function isInRange(range, index) { return range.start <= index && index < range.end; } var VirtualizedList = /** @class */ (function (_super) { tslib_1.__extends(VirtualizedList, _super); function VirtualizedList(props, context) { var _this = _super.call(this, props, context) || this; _this._root = Utilities_1.createRef(); _this._spacerElements = {}; _this._spacerRef = function (key, ref) { if (ref) { _this._spacerElements[key] = ref; } else { delete _this._spacerElements[key]; } }; _this._focusedIndex = -1; var _a = _this.props.initialViewportHeight // Start with the window height if not passed in props, this does not cause layout , initialViewportHeight = _a === void 0 ? window.innerHeight : _a // Start with the window height if not passed in props, this does not cause layout ; _this.state = { viewportHeight: initialViewportHeight, items: _this._renderItems(0, initialViewportHeight) }; return _this; } VirtualizedList.prototype.componentDidMount = function () { var _this = this; this._events.on(this._root, 'focus', this._onFocus, true); this.context.scrollContainer.registerVisibleCallback(function (scrollTop) { _this._render(scrollTop); }); this._updateObservedElements(); }; VirtualizedList.prototype.componentDidUpdate = function () { this._updateObservedElements(); }; VirtualizedList.prototype.componentWillUpdate = function () { for (var _i = 0, _a = Object.keys(this._spacerElements); _i < _a.length; _i++) { var key = _a[_i]; var ref = this._spacerElements[key]; this.context.scrollContainer.unobserve(ref); } }; VirtualizedList.prototype.render = function () { var className = this.props.className; var items = this.state.items; return (React.createElement("div", { className: Utilities_1.css('ms-VirtualizedList', className), ref: this._root }, items)); }; VirtualizedList.prototype._updateObservedElements = function () { // (Re-)register with the observer after every update, so we'll get an intersection event immediately if one of the spacer // elements is visible right now. for (var _i = 0, _a = Object.keys(this._spacerElements); _i < _a.length; _i++) { var key = _a[_i]; var ref = this._spacerElements[key]; this.context.scrollContainer.observe(ref); } }; VirtualizedList.prototype._renderItems = function (scrollTop, viewportHeight) { var _a = this.props, itemHeight = _a.itemHeight, items = _a.items, _b = _a.itemOverdraw, itemOverdraw = _b === void 0 ? 2 : _b; var ranges = []; // Calculate visible range var startIndex = Math.floor(Math.max(scrollTop / itemHeight - itemOverdraw, 0)); var endIndex = Math.floor(Math.min(startIndex + itemOverdraw * 2 + viewportHeight / itemHeight, items.length)); var visibleRange = { start: startIndex, end: endIndex }; ranges.push(visibleRange); // Focused item if (this._focusedIndex !== -1 && !isInRange(visibleRange, this._focusedIndex)) { var focusRange = { start: this._focusedIndex, end: this._focusedIndex + 1 }; if (this._focusedIndex < visibleRange.start) { ranges.unshift(focusRange); } else { ranges.push(focusRange); } } return this._renderRanges(ranges); }; VirtualizedList.prototype._renderRanges = function (ranges) { var _a = this.props, items = _a.items, onRenderItem = _a.onRenderItem; var result = []; // Assume ranges are sorted. var lastRenderedIndex = -1; for (var _i = 0, ranges_1 = ranges; _i < ranges_1.length; _i++) { var range = ranges_1[_i]; // Spacer item before range or between the last range and this one var isFirstRange = lastRenderedIndex === -1; if ((isFirstRange && range.start !== 0) || (!isFirstRange && lastRenderedIndex !== range.start)) { // Last range is not continuous with this one, // or the first range does not start from the beginning: insert spacer item var spacerStartIndex = isFirstRange ? 0 : lastRenderedIndex; var gapBetweenRanges = range.start - spacerStartIndex; if (gapBetweenRanges > 0) { result.push(this._renderSpacerItem(gapBetweenRanges, spacerStartIndex)); } } for (var i = range.start; i < range.end; ++i) { result.push(onRenderItem(items[i], i)); } lastRenderedIndex = range.end - 1; } // Insert final spacer item var itemCount = (items || []).length; if (lastRenderedIndex < itemCount - 1) { result.push(this._renderSpacerItem(itemCount - lastRenderedIndex, lastRenderedIndex)); } return result; }; VirtualizedList.prototype._renderSpacerItem = function (numberOfItems, index) { var _a = this.props, itemHeight = _a.itemHeight, _b = _a.items, items = _b === void 0 ? [] : _b, _c = _a.spacerItemTagName, ItemTag = _c === void 0 ? 'div' : _c; var spacerHeight = numberOfItems * itemHeight; var itemCount = items.length; var key; if (index === 0) { key = "spacer-start"; } else if (index + numberOfItems === itemCount) { key = "spacer-end"; } else { key = "spacer-item-" + (index + numberOfItems); } // tslint:disable-next-line:jsx-ban-props return React.createElement(ItemTag, { ref: this._spacerRef.bind(this, key), key: key, style: { height: spacerHeight } }); }; VirtualizedList.prototype._render = function (scrollTop) { scrollTop = Math.floor(scrollTop); this.setState({ items: this._renderItems(scrollTop, this.state.viewportHeight) }); }; VirtualizedList.prototype._onFocus = function (ev) { var target = ev.target; while (target !== this._root.current) { var indexString = target.getAttribute('data-selection-index'); if (indexString) { this._focusedIndex = Number(indexString); break; } target = Utilities_1.getParent(target); } }; VirtualizedList.contextTypes = ScrollContainer_1.ScrollContainerContextTypes; return VirtualizedList; }(Utilities_1.BaseComponent)); exports.VirtualizedList = VirtualizedList; }); //# sourceMappingURL=VirtualizedList.js.map