angular2-virtual-select
Version:
A native angular 2 select and multi select component with virtual scrolling to allow thousands of options in one select component
176 lines • 8.48 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var common_1 = require("@angular/common");
var VirtualScrollComponent = (function () {
function VirtualScrollComponent(element, renderer) {
this.element = element;
this.renderer = renderer;
this.items = [];
this.update = new core_1.EventEmitter();
this.change = new core_1.EventEmitter();
this.start = new core_1.EventEmitter();
this.end = new core_1.EventEmitter();
this.startupLoop = true;
}
VirtualScrollComponent.prototype.ngOnInit = function () {
this.onScrollListener = this.renderer.listen(this.element.nativeElement, 'scroll', this.refresh.bind(this));
this.scrollbarWidth = 0; // this.element.nativeElement.offsetWidth - this.element.nativeElement.clientWidth;
this.scrollbarHeight = 0; // this.element.nativeElement.offsetHeight - this.element.nativeElement.clientHeight;
};
VirtualScrollComponent.prototype.ngOnChanges = function (changes) {
this.previousStart = undefined;
this.previousEnd = undefined;
this.refresh();
};
VirtualScrollComponent.prototype.ngOnDestroy = function () {
// Check that listener has been attached properly:
// It may be undefined in some cases, e.g. if an exception is thrown, the component is
// not initialized properly but destroy may be called anyways (e.g. in testing).
if (this.onScrollListener !== undefined) {
// this removes the listener
this.onScrollListener();
}
};
VirtualScrollComponent.prototype.refresh = function () {
requestAnimationFrame(this.calculateItems.bind(this));
};
VirtualScrollComponent.prototype.scrollInto = function (item) {
var index = (this.items || []).indexOf(item);
if (index < 0 || index >= (this.items || []).length)
return;
var d = this.calculateDimensions();
this.element.nativeElement.scrollTop = Math.floor(index / d.itemsPerRow) *
d.childHeight - Math.max(0, (d.itemsPerCol - 1)) * d.childHeight;
this.refresh();
};
VirtualScrollComponent.prototype.countItemsPerRow = function () {
var offsetTop;
var itemsPerRow;
var children = this.contentElementRef.nativeElement.children;
for (itemsPerRow = 0; itemsPerRow < children.length; itemsPerRow++) {
if (offsetTop != undefined && offsetTop !== children[itemsPerRow].offsetTop)
break;
offsetTop = children[itemsPerRow].offsetTop;
}
return itemsPerRow;
};
VirtualScrollComponent.prototype.calculateDimensions = function () {
var el = this.element.nativeElement;
var content = this.contentElementRef.nativeElement;
var items = this.items || [];
var itemCount = items.length;
var viewWidth = el.clientWidth - this.scrollbarWidth;
var viewHeight = el.clientHeight - this.scrollbarHeight;
var contentDimensions;
if (this.childWidth == undefined || this.childHeight == undefined) {
contentDimensions = content.children[0] ? content.children[0].getBoundingClientRect() : {
width: viewWidth,
height: viewHeight
};
}
var childWidth = this.childWidth || contentDimensions.width;
var childHeight = this.childHeight || contentDimensions.height;
var itemsPerRow = Math.max(1, this.countItemsPerRow());
var itemsPerRowByCalc = Math.max(1, Math.floor(viewWidth / childWidth));
var itemsPerCol = Math.max(1, Math.floor(viewHeight / childHeight));
if (itemsPerCol === 1 && Math.floor(el.scrollTop / this.scrollHeight * itemCount) + itemsPerRowByCalc >= itemCount) {
itemsPerRow = itemsPerRowByCalc;
}
return {
itemCount: itemCount,
viewWidth: viewWidth,
viewHeight: viewHeight,
childWidth: childWidth,
childHeight: childHeight,
itemsPerRow: itemsPerRow,
itemsPerCol: itemsPerCol,
itemsPerRowByCalc: itemsPerRowByCalc
};
};
VirtualScrollComponent.prototype.calculateItems = function () {
var el = this.element.nativeElement;
var d = this.calculateDimensions();
var items = this.items || [];
this.scrollHeight = d.childHeight * d.itemCount / d.itemsPerRow;
if (this.element.nativeElement.scrollTop > this.scrollHeight) {
this.element.nativeElement.scrollTop = this.scrollHeight;
}
var indexByScrollTop = el.scrollTop / this.scrollHeight * d.itemCount / d.itemsPerRow;
var end = Math.min(d.itemCount, Math.ceil(indexByScrollTop) * d.itemsPerRow + d.itemsPerRow * (d.itemsPerCol + 1));
var maxStartEnd = end;
var modEnd = end % d.itemsPerRow;
if (modEnd) {
maxStartEnd = end + d.itemsPerRow - modEnd;
}
var maxStart = Math.max(0, maxStartEnd - d.itemsPerCol * d.itemsPerRow - d.itemsPerRow);
var start = Math.min(maxStart, Math.floor(indexByScrollTop) * d.itemsPerRow);
this.topPadding = d.childHeight * Math.ceil(start / d.itemsPerRow);
if (start !== this.previousStart || end !== this.previousEnd) {
// update the scroll list
this.update.emit(items.slice(start, end));
// emit 'start' event
if (start !== this.previousStart && this.startupLoop === false) {
this.start.emit({ start: start, end: end });
}
// emit 'end' event
if (end !== this.previousEnd && this.startupLoop === false) {
this.end.emit({ start: start, end: end });
}
this.previousStart = start;
this.previousEnd = end;
if (this.startupLoop === true) {
this.refresh();
}
else {
this.change.emit({ start: start, end: end });
}
}
else if (this.startupLoop === true) {
this.startupLoop = false;
this.refresh();
}
};
return VirtualScrollComponent;
}());
VirtualScrollComponent.decorators = [
{ type: core_1.Component, args: [{
selector: 'virtual-scroll',
template: "\n <div class=\"total-padding\" [style.height]=\"scrollHeight + 'px'\"></div>\n <div class=\"scrollable-content\" #content [style.transform]=\"'translateY(' + topPadding + 'px)'\">\n <ng-content></ng-content>\n </div>\n ",
styles: ["\n :host {\n overflow: auto;\n overflow-y: auto;\n position: relative;\n -webkit-overflow-scrolling: touch;\n }\n .scrollable-content {\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n position: absolute;\n }\n .total-padding {\n width: 1px;\n opacity: 0;\n }\n "]
},] },
];
/** @nocollapse */
VirtualScrollComponent.ctorParameters = function () { return [
{ type: core_1.ElementRef, },
{ type: core_1.Renderer, },
]; };
VirtualScrollComponent.propDecorators = {
'items': [{ type: core_1.Input },],
'scrollbarWidth': [{ type: core_1.Input },],
'scrollbarHeight': [{ type: core_1.Input },],
'childWidth': [{ type: core_1.Input },],
'childHeight': [{ type: core_1.Input },],
'update': [{ type: core_1.Output },],
'change': [{ type: core_1.Output },],
'start': [{ type: core_1.Output },],
'end': [{ type: core_1.Output },],
'contentElementRef': [{ type: core_1.ViewChild, args: ['content', { read: core_1.ElementRef },] },],
};
exports.VirtualScrollComponent = VirtualScrollComponent;
var VirtualScrollModule = (function () {
function VirtualScrollModule() {
}
return VirtualScrollModule;
}());
VirtualScrollModule.decorators = [
{ type: core_1.NgModule, args: [{
imports: [common_1.CommonModule],
exports: [VirtualScrollComponent],
declarations: [VirtualScrollComponent]
},] },
];
/** @nocollapse */
VirtualScrollModule.ctorParameters = function () { return []; };
exports.VirtualScrollModule = VirtualScrollModule;
//# sourceMappingURL=virtual-scroll.js.map