UNPKG

vanilla-recycler-view

Version:

high performance UI rendering library for web browser

326 lines 12.2 kB
"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