|
|
|
/**
|
|
|
|
* 边滚动边加载的列表控件
|
|
|
|
*
|
|
|
|
* Created by GUY on 2017/5/23.
|
|
|
|
* @class BI.ListView
|
|
|
|
* @extends BI.Widget
|
|
|
|
*/
|
|
|
|
import { Widget, shortcut } from "../../core";
|
|
|
|
@shortcut()
|
|
|
|
export default class ListView extends Widget {
|
|
|
|
props() {
|
|
|
|
return {
|
|
|
|
baseCls: "bi-list-view",
|
|
|
|
overscanHeight: 100,
|
|
|
|
blockSize: 10,
|
|
|
|
scrollTop: 0,
|
|
|
|
el: {},
|
|
|
|
items: [],
|
|
|
|
itemFormatter: (item, index)=> {
|
|
|
|
return item;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
init() {
|
|
|
|
this.renderedIndex = -1;
|
|
|
|
this.cache = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
static xtype = "bi.list_view";
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { el } = this.options;
|
|
|
|
|
|
|
|
return {
|
|
|
|
type: "bi.vertical",
|
|
|
|
items: [BI.extend({
|
|
|
|
type: "bi.vertical",
|
|
|
|
scrolly: false,
|
|
|
|
ref: (_ref)=> {
|
|
|
|
this.container = _ref;
|
|
|
|
},
|
|
|
|
}, el)],
|
|
|
|
element: this,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// mounted之后绑定事件
|
|
|
|
mounted() {
|
|
|
|
const o = this.options;
|
|
|
|
// 这里无法进行结构,因为存在赋值操作,如果使用结构则this.options的值不会跟随变化
|
|
|
|
o.items = BI.isFunction(o.items) ? this.__watch(o.items, (context, newValue)=> {
|
|
|
|
this.populate(newValue);
|
|
|
|
}) : o.items;
|
|
|
|
this._populate();
|
|
|
|
this.element.scroll((e)=> {
|
|
|
|
o.scrollTop = this.element.scrollTop();
|
|
|
|
this._calculateBlocksToRender();
|
|
|
|
});
|
|
|
|
let lastWidth = this.element.width(),
|
|
|
|
lastHeight = this.element.height();
|
|
|
|
BI.ResizeDetector.addResizeListener(this, ()=> {
|
|
|
|
if (!this.element.is(":visible")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const width = this.element.width(),
|
|
|
|
height = this.element.height();
|
|
|
|
if (width !== lastWidth || height !== lastHeight) {
|
|
|
|
lastWidth = width;
|
|
|
|
lastHeight = height;
|
|
|
|
this._calculateBlocksToRender();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_renderMoreIf() {
|
|
|
|
const { scrollTop, overscanHeight, blockSize, items, itemFormatter } = this.options;
|
|
|
|
const height = this.element.height();
|
|
|
|
const minContentHeight = scrollTop + height + overscanHeight;
|
|
|
|
let index = (this.cache[this.renderedIndex] && (this.cache[this.renderedIndex].index + blockSize)) || 0;
|
|
|
|
let cnt = this.renderedIndex + 1;
|
|
|
|
let lastHeight;
|
|
|
|
|
|
|
|
const getElementHeight = ()=> {
|
|
|
|
return this.container.element.height();
|
|
|
|
}
|
|
|
|
|
|
|
|
lastHeight = getElementHeight();
|
|
|
|
while ((lastHeight) < minContentHeight && index < items.length) {
|
|
|
|
const itemsArr = items.slice(index, index + blockSize);
|
|
|
|
this.container.addItems(itemsArr.map((item, i)=> {
|
|
|
|
return itemFormatter(item, index + i);
|
|
|
|
}), this);
|
|
|
|
const addedHeight = getElementHeight() - lastHeight;
|
|
|
|
this.cache[cnt] = {
|
|
|
|
index: index,
|
|
|
|
scrollTop: lastHeight,
|
|
|
|
height: addedHeight,
|
|
|
|
};
|
|
|
|
this.renderedIndex = cnt;
|
|
|
|
cnt++;
|
|
|
|
index += blockSize;
|
|
|
|
lastHeight = getElementHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_calculateBlocksToRender() {
|
|
|
|
// BI-115750 不可见状态下依赖元素实际尺寸构造的线段树会分段错误,所以不进行后续计算和线段树的初始化。
|
|
|
|
// 这样从不可见状态变为可见状态能够重新触发线段树初始化
|
|
|
|
if (!this.element.is(":visible")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._renderMoreIf();
|
|
|
|
}
|
|
|
|
|
|
|
|
_populate(items) {
|
|
|
|
const { scrollTop } = this.options;
|
|
|
|
if (items && this.options.items !== items) {
|
|
|
|
this.options.items = items;
|
|
|
|
}
|
|
|
|
this._calculateBlocksToRender();
|
|
|
|
this.element.scrollTop(scrollTop);
|
|
|
|
}
|
|
|
|
|
|
|
|
restore() {
|
|
|
|
this.renderedIndex = -1;
|
|
|
|
this.container.empty();
|
|
|
|
this.cache = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
scrollTo(scrollTop) {
|
|
|
|
this.options.scrollTop = scrollTop;
|
|
|
|
this._calculateBlocksToRender();
|
|
|
|
this.element.scrollTop(scrollTop);
|
|
|
|
}
|
|
|
|
|
|
|
|
populate(items) {
|
|
|
|
if (items && this.options.items !== items) {
|
|
|
|
this.restore();
|
|
|
|
}
|
|
|
|
this._populate(items);
|
|
|
|
}
|
|
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
BI.ResizeDetector.removeResizeListener(this);
|
|
|
|
this.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|