UNPKG

xl-infinite-tree

Version:

A browser-ready tree library that can efficiently display a large amount of data using infinite scrolling.

392 lines (305 loc) 13.9 kB
'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _events = require('events'); var _ensureArray = require('./ensure-array'); var _ensureArray2 = _interopRequireDefault(_ensureArray); var _browser = require('./browser'); var _dom = require('./dom'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var ie = (0, _browser.getIEVersion)(); var Clusterize = function (_EventEmitter) { _inherits(Clusterize, _EventEmitter); function Clusterize(options) { _classCallCheck(this, Clusterize); var _this = _possibleConstructorReturn(this, _EventEmitter.call(this)); _this.options = { rowsInBlock: 50, blocksInCluster: 4, tag: null, emptyClass: '', emptyText: '', keepParity: true }; _this.state = { lastClusterIndex: -1, itemHeight: 0, blockHeight: 0, clusterHeight: 0 }; _this.scrollElement = null; _this.contentElement = null; _this.rows = []; _this.cache = {}; _this.scrollEventListener = function () { var debounce = null; return function () { var isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; if (isMac) { if (_this.contentElement.style.pointerEvents !== 'none') { _this.contentElement.style.pointerEvents = 'none'; } if (debounce) { clearTimeout(debounce); debounce = null; } debounce = setTimeout(function () { debounce = null; _this.contentElement.style.pointerEvents = 'auto'; }, 50); } var clusterIndex = _this.getCurrentClusterIndex(); if (_this.state.lastClusterIndex !== clusterIndex) { _this.changeDOM(); } _this.state.lastClusterIndex = clusterIndex; }; }(); _this.resizeEventListener = function () { var debounce = null; return function () { if (debounce) { clearTimeout(debounce); debounce = null; } debounce = setTimeout(function () { var prevItemHeight = _this.state.itemHeight; var current = _this.computeHeight(); if (current.itemHeight > 0 && prevItemHeight !== current.itemHeight) { _this.state = _extends({}, _this.state, current); _this.update(_this.rows); } }, 100); }; }(); if (!(_this instanceof Clusterize)) { var _ret; return _ret = new Clusterize(options), _possibleConstructorReturn(_this, _ret); } _this.options = Object.keys(_this.options).reduce(function (acc, key) { if (options[key] !== undefined) { acc[key] = options[key]; } else { acc[key] = _this.options[key]; } return acc; }, {}); _this.scrollElement = options.scrollElement; _this.contentElement = options.contentElement; // Keep focus on the scrolling content if (!_this.contentElement.hasAttribute('tabindex')) { _this.contentElement.setAttribute('tabindex', 0); } if (Array.isArray(options.rows)) { _this.rows = options.rows; } else { _this.rows = []; var nodes = _this.contentElement.children; var length = nodes.length; for (var i = 0; i < length; ++i) { var node = nodes[i]; _this.rows.push(node.outerHTML || ''); } } // Remember scroll position var scrollTop = _this.scrollElement.scrollTop; _this.changeDOM(); // Restore scroll position _this.scrollElement.scrollTop = scrollTop; (0, _dom.addEventListener)(_this.scrollElement, 'scroll', _this.scrollEventListener); (0, _dom.addEventListener)(window, 'resize', _this.resizeEventListener); return _this; } Clusterize.prototype.destroy = function destroy(clean) { (0, _dom.removeEventListener)(this.scrollElement, 'scroll', this.scrollEventListener); (0, _dom.removeEventListener)(window, 'resize', this.resizeEventListener); var rows = clean ? this.generateEmptyRow() : this.rows(); this.setContent(rows.join('')); }; Clusterize.prototype.update = function update(rows) { this.rows = (0, _ensureArray2['default'])(rows); // Remember scroll position var scrollTop = this.scrollElement.scrollTop; if (this.rows.length * this.state.itemHeight < scrollTop) { this.scrollElement.scrollTop = 0; this.state.lastClusterIndex = 0; } this.changeDOM(); // Restore scroll position this.scrollElement.scrollTop = scrollTop; }; Clusterize.prototype.clear = function clear() { this.rows = []; this.update(); }; Clusterize.prototype.append = function append(rows) { rows = (0, _ensureArray2['default'])(rows); if (!rows.length) { return; } this.rows = this.rows.concat(rows); this.changeDOM(); }; Clusterize.prototype.prepend = function prepend(rows) { rows = (0, _ensureArray2['default'])(rows); if (!rows.length) { return; } this.rows = rows.concat(this.rows); this.changeDOM(); }; Clusterize.prototype.computeHeight = function computeHeight() { if (!this.rows.length) { return { clusterHeight: 0, blockHeight: this.state.blockHeight, itemHeight: this.state.itemHeight }; } else { var nodes = this.contentElement.children; var node = nodes[Math.floor(nodes.length / 2)]; var itemHeight = node.offsetHeight; if (this.options.tag === 'tr' && (0, _dom.getElementStyle)(this.contentElement, 'borderCollapse') !== 'collapse') { itemHeight += parseInt((0, _dom.getElementStyle)(this.contentElement, 'borderSpacing'), 10) || 0; } if (this.options.tag !== 'tr') { var marginTop = parseInt((0, _dom.getElementStyle)(node, 'marginTop'), 10) || 0; var marginBottom = parseInt((0, _dom.getElementStyle)(node, 'marginBottom'), 10) || 0; itemHeight += Math.max(marginTop, marginBottom); } var blockHeight = itemHeight * this.options.rowsInBlock; var clusterHeight = blockHeight * this.options.blocksInCluster; return { itemHeight: itemHeight, blockHeight: blockHeight, clusterHeight: clusterHeight }; } }; Clusterize.prototype.getCurrentClusterIndex = function getCurrentClusterIndex() { var _state = this.state, blockHeight = _state.blockHeight, clusterHeight = _state.clusterHeight; if (!blockHeight || !clusterHeight) { return 0; } return Math.floor(this.scrollElement.scrollTop / (clusterHeight - blockHeight)) || 0; }; Clusterize.prototype.generateEmptyRow = function generateEmptyRow() { var _options = this.options, tag = _options.tag, emptyText = _options.emptyText, emptyClass = _options.emptyClass; if (!tag || !emptyText) { return []; } var emptyRow = document.createElement(tag); emptyRow.className = emptyClass; if (tag === 'tr') { var td = document.createElement('td'); td.colSpan = 100; td.appendChild(document.createTextNode(emptyText)); emptyRow.appendChild(td); } else { emptyRow.appendChild(document.createTextNode(emptyText)); } return [emptyRow.outerHTML]; }; Clusterize.prototype.renderExtraTag = function renderExtraTag(className, height) { var tag = document.createElement(this.options.tag); var prefix = 'infinite-tree-'; tag.className = [prefix + 'extra-row', prefix + className].join(' '); if (height) { tag.style.height = height + 'px'; } return tag.outerHTML; }; Clusterize.prototype.changeDOM = function changeDOM() { if (!this.state.clusterHeight && this.rows.length > 0) { if (ie && ie <= 9 && !this.options.tag) { this.options.tag = this.rows[0].match(/<([^>\s/]*)/)[1].toLowerCase(); } if (this.contentElement.children.length <= 1) { this.cache.content = this.setContent(this.rows[0] + this.rows[0] + this.rows[0]); } if (!this.options.tag) { this.options.tag = this.contentElement.children[0].tagName.toLowerCase(); } this.state = _extends({}, this.state, this.computeHeight()); } var topOffset = 0; var bottomOffset = 0; var rows = []; if (this.rows.length < this.options.rowsInBlock) { rows = this.rows.length > 0 ? this.rows : this.generateEmptyRow(); } else { var rowsInCluster = this.options.rowsInBlock * this.options.blocksInCluster; var clusterIndex = this.getCurrentClusterIndex(); var visibleStart = Math.max((rowsInCluster - this.options.rowsInBlock) * clusterIndex, 0); var visibleEnd = visibleStart + rowsInCluster; topOffset = Math.max(visibleStart * this.state.itemHeight, 0); bottomOffset = Math.max((this.rows.length - visibleEnd) * this.state.itemHeight, 0); // Returns a shallow copy of the rows selected from `visibleStart` to `visibleEnd` (`visibleEnd` not included). rows = this.rows.slice(visibleStart, visibleEnd); } var content = rows.join(''); var contentChanged = this.checkChanges('content', content); var topOffsetChanged = this.checkChanges('top', topOffset); var bottomOffsetChanged = this.checkChanges('bottom', bottomOffset); if (contentChanged || topOffsetChanged) { var layout = []; if (topOffset > 0) { if (this.options.keepParity) { layout.push(this.renderExtraTag('keep-parity')); } layout.push(this.renderExtraTag('top-space', topOffset)); } layout.push(content); if (bottomOffset > 0) { layout.push(this.renderExtraTag('bottom-space', bottomOffset)); } this.emit('clusterWillChange'); this.setContent(layout.join('')); this.emit('clusterDidChange'); } else if (bottomOffsetChanged) { this.contentElement.lastChild.style.height = bottomOffset + 'px'; } }; Clusterize.prototype.setContent = function setContent(content) { // For IE 9 and older versions if (ie && ie <= 9 && this.options.tag === 'tr') { var div = document.createElement('div'); div.innerHTML = '<table><tbody>' + content + '</tbody></table>'; var lastChild = this.contentElement.lastChild; while (lastChild) { this.contentElement.removeChild(lastChild); lastChild = this.contentElement.lastChild; } var rowsNodes = this.getChildNodes(div.firstChild.firstChild); while (rowsNodes.length) { this.contentElement.appendChild(rowsNodes.shift()); } } else { this.contentElement.innerHTML = content; } }; Clusterize.prototype.getChildNodes = function getChildNodes(tag) { var childNodes = tag.children; var nodes = []; var length = childNodes.length; for (var i = 0; i < length; i++) { nodes.push(childNodes[i]); } return nodes; }; Clusterize.prototype.checkChanges = function checkChanges(type, value) { var changed = value !== this.cache[type]; this.cache[type] = value; return changed; }; return Clusterize; }(_events.EventEmitter); exports['default'] = Clusterize;