@uifabric/experiments
Version:
Experimental React components for building experiences for Office 365.
158 lines • 8.03 kB
JavaScript
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