fix(virtual-scroll): JSX can render headers and footers

This commit is contained in:
Manu Mtz.-Almeida
2018-05-10 17:17:33 +02:00
parent 50021cd0be
commit 012127dd7c
7 changed files with 67 additions and 44 deletions

View File

@ -17,10 +17,10 @@ export class VirtualScroll {
private el: ElementRef,
public cd: ChangeDetectorRef,
) {
this.el.nativeElement.itemRender = this.itemRender.bind(this);
this.el.nativeElement.nodeRender = this.nodeRender.bind(this);
}
private itemRender(el: HTMLElement|null, cell: any, index?: number) {
private nodeRender(el: HTMLElement|null, cell: any, index?: number) {
if (!el) {
const node = this.itmTmp.viewContainer.createEmbeddedView(
this.getComponent(cell.type),

View File

@ -99,7 +99,6 @@ import {
HeaderFn,
ItemHeightFn,
ItemRenderFn,
NodeHeightFn,
} from './components/virtual-scroll/virtual-scroll-utils';
declare global {
@ -7213,16 +7212,17 @@ declare global {
*/
'headerFn': HeaderFn;
'itemHeight': ItemHeightFn;
'itemRender': ItemRenderFn;
/**
* The data that builds the templates within the virtual scroll. It's important to note that when this data has changed, then the entire virtual scroll is reset, which is an expensive operation and should be avoided if possible.
*/
'items': any[];
'markDirty': (offset: number, len?: number) => void;
'markDirtyTail': () => void;
'nodeHeight': NodeHeightFn;
'nodeRender': ItemRenderFn;
'positionForItem': (index: number) => number;
'renderer': (item: any) => JSX.Element;
'renderFooter': (item: any, index: number) => JSX.Element;
'renderHeader': (item: any, index: number) => JSX.Element;
'renderItem': (item: any, index: number) => JSX.Element;
}
}
@ -7267,13 +7267,14 @@ declare global {
*/
'headerFn'?: HeaderFn;
'itemHeight'?: ItemHeightFn;
'itemRender'?: ItemRenderFn;
/**
* The data that builds the templates within the virtual scroll. It's important to note that when this data has changed, then the entire virtual scroll is reset, which is an expensive operation and should be avoided if possible.
*/
'items'?: any[];
'nodeHeight'?: NodeHeightFn;
'renderer'?: (item: any) => JSX.Element;
'nodeRender'?: ItemRenderFn;
'renderFooter'?: (item: any, index: number) => JSX.Element;
'renderHeader'?: (item: any, index: number) => JSX.Element;
'renderItem'?: (item: any, index: number) => JSX.Element;
}
}
}

View File

@ -260,11 +260,6 @@ and what data to give to the header template. The function must return
#### itemRender
#### items
@ -275,12 +270,22 @@ entire virtual scroll is reset, which is an expensive operation and
should be avoided if possible.
#### nodeHeight
#### nodeRender
#### renderer
#### renderFooter
#### renderHeader
#### renderItem
@ -364,11 +369,6 @@ and what data to give to the header template. The function must return
#### item-render
#### items
@ -379,12 +379,22 @@ entire virtual scroll is reset, which is an expensive operation and
should be avoided if possible.
#### node-height
#### node-render
#### renderer
#### render-footer
#### render-header
#### render-item

View File

@ -82,7 +82,7 @@
return el;
}
virtual.itemRender = (el, cell) => {
virtual.nodeRender = (el, cell) => {
if (cell.type === 0) return renderItem(el, cell.value);
return renderHeader(el, cell.value);
};

View File

@ -56,7 +56,7 @@
return el;
}
virtual.itemRender = (el, cell) => {
virtual.nodeRender = (el, cell) => {
if (cell.type === 0) return renderItem(el, cell.value);
return renderHeader(el, cell.value);
};

View File

@ -40,7 +40,6 @@ export interface VirtualNode {
const MIN_READS = 2;
export type NodeHeightFn = (node: VirtualNode, index: number) => number;
export type HeaderFn = (item: any, index: number, items: any[]) => string | null;
export type ItemHeightFn = (item: any, index?: number) => number;
export type ItemRenderFn = (el: HTMLElement|null, cell: Cell, domIndex?: number) => HTMLElement;
@ -104,7 +103,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
export function doRender(
el: HTMLElement,
itemRender: ItemRenderFn,
nodeRender: ItemRenderFn,
dom: VirtualNode[],
updateCellHeight: Function
) {
@ -119,9 +118,9 @@ export function doRender(
if (node.change === NodeChange.Cell) {
if (i < childrenNu) {
child = children[i] as HTMLElement;
itemRender(child, cell, i);
nodeRender(child, cell, i);
} else {
child = itemRender(null, cell, i);
child = nodeRender(null, cell, i);
child.classList.add('virtual-item');
el.appendChild(child);
}

View File

@ -1,10 +1,10 @@
import { Component, Element, EventListenerEnable, Listen, Method, Prop, Watch } from '@stencil/core';
import { QueueController } from '../../interface';
import { Cell, DomRenderFn, HeaderFn, ItemHeightFn,
ItemRenderFn, NodeHeightFn, Range,
ItemRenderFn, Range,
VirtualNode, calcCells, calcHeightIndex, doRender,
findCellIndex, getRange, getShouldUpdate, getViewport,
inplaceUpdate, positionForIndex, resizeBuffer, updateVDom } from './virtual-scroll-utils';
inplaceUpdate, positionForIndex, resizeBuffer, updateVDom, CellType } from './virtual-scroll-utils';
@Component({
@ -98,11 +98,15 @@ export class VirtualScroll {
* should be avoided if possible.
*/
@Prop() items?: any[];
@Prop() renderer?: (item: any) => JSX.Element;
@Prop() nodeHeight?: NodeHeightFn;
@Prop() itemHeight?: ItemHeightFn;
@Prop() itemRender?: ItemRenderFn;
// JSX API
@Prop() renderItem?: (item: any, index: number) => JSX.Element;
@Prop() renderHeader?: (item: any, index: number) => JSX.Element;
@Prop() renderFooter?: (item: any, index: number) => JSX.Element;
// Low level API
@Prop() nodeRender?: ItemRenderFn;
@Prop() domRender?: DomRenderFn;
@Watch('itemHeight')
@ -255,11 +259,11 @@ export class VirtualScroll {
);
// write DOM
if (this.itemRender) {
doRender(this.el, this.itemRender, this.virtualDom, this.updateCellHeight.bind(this));
if (this.nodeRender) {
doRender(this.el, this.nodeRender, this.virtualDom, this.updateCellHeight.bind(this));
} else if (this.domRender) {
this.domRender(this.virtualDom);
} else if (this.renderer) {
} else if (this.renderItem) {
this.el.forceUpdate();
}
if (this.heightChanged) {
@ -370,24 +374,33 @@ export class VirtualScroll {
}
}
renderVirtualNode(node: VirtualNode) {
const cell = node.cell;
switch(cell.type) {
case CellType.Item: return this.renderItem!(cell.value, cell.index);
case CellType.Header: return this.renderHeader!(cell.value, cell.index);
case CellType.Footer: return this.renderFooter!(cell.value, cell.index);
}
}
render() {
const renderer = this.renderer;
if (renderer) {
return this.virtualDom.map((dom) => {
const item = renderer(dom.cell.value) as any;
const renderItem = this.renderItem;
if (renderItem) {
return this.virtualDom.map((node) => {
const item = this.renderVirtualNode(node) as any;
const classes = ['virtual-item'];
if (!item.vattrs) {
item.vattrs = {};
}
item.vattrs.class += ' virtual-item';
if (!dom.visible) {
if (!node.visible) {
classes.push('virtual-loading');
}
item.vattrs.class += ' ' + classes.join(' ');
if (!item.vattrs.style) {
item.vattrs.style = {};
}
item.vattrs.style['transform'] = `translate3d(0,${dom.top}px,0)`;
item.vattrs.style['transform'] = `translate3d(0,${node.top}px,0)`;
return item;
});
}