vanilla-recycler-view
Version:
high performance UI rendering library for web browser
326 lines • 12.2 kB
JavaScript
"use strict";
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VanillaRecyclerView = exports.DIRECTION = exports.DEFAULT_ITEM_SIZE = void 0;
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
require("../modules/zoom-listener");
var _1 = require("./");
exports.DEFAULT_ITEM_SIZE = 50;
/**
* @deprecated
*/
var DIRECTION;
(function (DIRECTION) {
DIRECTION["VERTICAL"] = "vertical";
DIRECTION["HORIZONTAL"] = "horizontal";
})(DIRECTION = exports.DIRECTION || (exports.DIRECTION = {}));
var VanillaRecyclerView = /** @class */ (function () {
function VanillaRecyclerView(root, options) {
var _this = this;
/* all virtual elements */
this.virtualElements = [];
/* current mounted virtual elements */
this.mountedVirtualElements = [];
/* pending unmount */
this.pendingUnmount = [];
/* unmounted, reusable elements */
this.reusables = [];
/* allocate arguments */
this.root = root;
/* define default direction */
if (options.direction) {
this._direction = options.direction;
}
else {
this._direction = DIRECTION.VERTICAL;
}
/* define default preload */
if (options.preload) {
this._preload = options.preload;
}
else {
this._preload = exports.DEFAULT_ITEM_SIZE;
}
/* define default size */
if (options.size) {
this._size = options.size;
}
else {
this._size = exports.DEFAULT_ITEM_SIZE;
}
/* allocate renderer */
this._renderer = options.renderer;
/* apply class to element */
this.root.classList.add('recycler_view_root');
if (this._direction === DIRECTION.VERTICAL) {
this.root.classList.add(DIRECTION.VERTICAL);
}
else if (this._direction === DIRECTION.HORIZONTAL) {
this.root.classList.add(DIRECTION.HORIZONTAL);
}
/* create wrapper element for scroll area */
this.container = document.createElement('div');
this.container.classList.add('recycler_view_container');
/* create virtual element per data */
this.setData(options.data);
/* append element */
this.root.appendChild(this.container);
// bind scroll, zoom event
this.root.addEventListener('scroll', this.onScroll.bind(this));
document.body.addEventListener('zoom', function () { return _this.onScroll(); });
}
VanillaRecyclerView.prototype.onScroll = function () {
var _this = this;
var _a = this.root, scrollTop = _a.scrollTop, scrollLeft = _a.scrollLeft;
var _b = this.root.getBoundingClientRect(), screenHeight = _b.height, screenWidth = _b.width;
var max = this.getMaxScrollSize();
var maxScrollSize = (function () {
var clientSize = 0;
switch (_this._direction) {
case DIRECTION.VERTICAL:
clientSize = _this.root.clientHeight;
break;
case DIRECTION.HORIZONTAL:
clientSize = _this.root.clientWidth;
break;
}
return max > clientSize ? max : clientSize;
})();
var currentScrollSize = 0;
var startSize = 0;
var endSize = 0;
/* calculate direction */
switch (this._direction) {
case DIRECTION.VERTICAL:
currentScrollSize = scrollTop + screenHeight;
startSize = scrollTop - this._preload;
endSize = scrollTop + screenHeight + this._preload;
break;
case DIRECTION.HORIZONTAL:
currentScrollSize = scrollLeft + screenWidth;
startSize = scrollLeft - this._preload;
endSize = scrollLeft + screenWidth + this._preload;
break;
}
// todo fix
// if (currentScrollSize > maxScrollSize) {
// switch (this._direction) {
// case DIRECTION.VERTICAL:
// this.root.scrollTop = maxScrollSize - screenHeight;
// break;
// case DIRECTION.HORIZONTAL:
// this.root.scrollLeft = maxScrollSize - screenWidth;
// break;
// }
// console.log(currentScrollSize, maxScrollSize);
// this.onScroll();
// return;
// }
/**
* todo
* use map to save some performance
*/
var shouldMount = this.virtualElements.filter(function (virtualDom) {
return (virtualDom.start >= startSize &&
virtualDom.start + virtualDom.size <= endSize);
});
var mounted = [];
var toMount = [];
shouldMount.forEach(function (virtualDom) {
if (_this.mountedVirtualElements.indexOf(virtualDom) === -1) {
toMount.push(virtualDom);
}
else {
mounted.push(virtualDom);
}
});
var toUnmount = this.pendingUnmount.concat(this.mountedVirtualElements.filter(function (virtualDom) { return shouldMount.indexOf(virtualDom) === -1; }));
/**
* unmount and save reusable dom
*/
toUnmount.forEach(function (virtualDom) {
if (virtualDom.isMounted()) {
var reusable = virtualDom.unmountRenderer();
_this.reusables.push(reusable);
}
});
/**
* mount
*/
toMount.forEach(function (virtualDom) {
var reusable = (function () {
var _a;
var reusable = _this.getNextReusable();
var refreshed = reusable &&
reusable.renderer.onMount &&
reusable.renderer.onMount({
api: _this,
data: virtualDom.data,
index: virtualDom.index,
});
if (reusable && refreshed) {
return reusable;
}
else {
(_a = reusable === null || reusable === void 0 ? void 0 : reusable.wrapperElement.parentElement) === null || _a === void 0 ? void 0 : _a.removeChild(reusable.wrapperElement);
return _this.createReusable(virtualDom);
}
})();
virtualDom.mountRenderer(reusable);
});
/**
* update mounted renderer position
*/
mounted.forEach(function (virtualDom) {
virtualDom.updatePosition();
});
/**
* remove overflowed reusables
*/
this.reusables.forEach(function (reusable) {
var _a;
var start = 0;
switch (_this._direction) {
case DIRECTION.VERTICAL:
start = _1.parsePx(reusable.wrapperElement.style.top) + screenHeight;
break;
case DIRECTION.HORIZONTAL:
start = _1.parsePx(reusable.wrapperElement.style.left) + screenWidth;
break;
}
if (start > maxScrollSize) {
(_a = reusable.wrapperElement.parentElement) === null || _a === void 0 ? void 0 : _a.removeChild(reusable.wrapperElement);
}
});
this.pendingUnmount = [];
this.mountedVirtualElements = shouldMount;
};
/**
* parse size option
* @param data
*/
VanillaRecyclerView.prototype.getSize = function (index, data) {
if (typeof this._size === 'function') {
return this._size({ api: this, data: data, index: index });
}
else {
return this._size;
}
};
VanillaRecyclerView.prototype.createReusable = function (virtualElement) {
var container = document.createElement('div');
container.classList.add('recycler_view_item');
var renderer = new this._renderer();
renderer.initialize({
api: this,
data: virtualElement.data,
index: virtualElement.index,
});
var element = renderer.getLayout();
container.appendChild(element);
return {
wrapperElement: container,
renderer: renderer,
};
};
VanillaRecyclerView.prototype.getNextReusable = function () {
if (this.reusables && this.reusables.length) {
return this.reusables.splice(0, 1)[0];
}
else {
return null;
}
};
VanillaRecyclerView.prototype.setData = function (data) {
var _this = this;
this.reusables = [];
this.pendingUnmount = this.virtualElements.slice();
this.virtualElements = [];
this.calculateSize();
this.onScroll();
this.virtualElements = data.map(function (data) { return new _1.VirtualElement(_this, data); });
this.calculateSize();
this.onScroll();
};
VanillaRecyclerView.prototype.calculateSize = function () {
var _this = this;
var viewSize = this.virtualElements.reduce(function (start, virtualDom, index) {
var currentSize = _this.getSize(index, virtualDom.data);
virtualDom.setIndex(index);
virtualDom.setPosition(start, currentSize);
return start + currentSize;
}, 0);
switch (this._direction) {
case DIRECTION.VERTICAL:
this.container.style.height = _1.toPx(viewSize);
break;
case DIRECTION.HORIZONTAL:
this.container.style.width = _1.toPx(viewSize);
break;
default:
throw new Error('not supported direction');
}
};
VanillaRecyclerView.prototype.getMaxScrollSize = function () {
switch (this._direction) {
case DIRECTION.VERTICAL:
return _1.parsePx(this.container.style.height);
case DIRECTION.HORIZONTAL:
return _1.parsePx(this.container.style.width);
}
};
/**
* @deprecated
*/
VanillaRecyclerView.prototype.splice = function (start, end) {
if (end === undefined) {
this.pendingUnmount = this.virtualElements.splice(start);
}
else {
this.pendingUnmount = this.virtualElements.splice(start, end);
}
this.calculateSize();
this.onScroll();
};
/**
* @deprecated
*/
VanillaRecyclerView.prototype.insert = function (index) {
var _this = this;
var data = [];
for (var _i = 1; _i < arguments.length; _i++) {
data[_i - 1] = arguments[_i];
}
var prefix = this.virtualElements.slice(0, index);
var surfix = this.virtualElements.slice(index);
var virtualElements = data.map(function (data) { return new _1.VirtualElement(_this, data); });
this.virtualElements = __spreadArrays(prefix, virtualElements, surfix);
this.calculateSize();
this.onScroll();
};
/**
* @deprecated
*/
VanillaRecyclerView.prototype.push = function () {
var _a;
var _this = this;
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
var virtualElements = items.map(function (item) { return new _1.VirtualElement(_this, item); });
(_a = this.virtualElements).push.apply(_a, virtualElements);
this.calculateSize();
this.onScroll();
};
return VanillaRecyclerView;
}());
exports.VanillaRecyclerView = VanillaRecyclerView;
//# sourceMappingURL=recycler-view.js.map