From 012127dd7c9c55a347ff1248d72c81a263f0bc46 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Thu, 10 May 2018 17:17:33 +0200 Subject: [PATCH] fix(virtual-scroll): JSX can render headers and footers --- .../virtual-scroll/virtual-scroll.ts | 4 +- core/src/components.d.ts | 15 ++++--- core/src/components/virtual-scroll/readme.md | 38 ++++++++++------ .../components/virtual-scroll/test/basic.html | 2 +- .../components/virtual-scroll/test/cards.html | 2 +- .../virtual-scroll/virtual-scroll-utils.tsx | 7 ++- .../virtual-scroll/virtual-scroll.tsx | 43 ++++++++++++------- 7 files changed, 67 insertions(+), 44 deletions(-) diff --git a/angular/src/directives/virtual-scroll/virtual-scroll.ts b/angular/src/directives/virtual-scroll/virtual-scroll.ts index 3a76fce166..9b5eedcad8 100644 --- a/angular/src/directives/virtual-scroll/virtual-scroll.ts +++ b/angular/src/directives/virtual-scroll/virtual-scroll.ts @@ -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), diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 4e670eb252..515a96d1f1 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -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; } } } diff --git a/core/src/components/virtual-scroll/readme.md b/core/src/components/virtual-scroll/readme.md index 5bd57115c1..44d0d4e943 100644 --- a/core/src/components/virtual-scroll/readme.md +++ b/core/src/components/virtual-scroll/readme.md @@ -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 diff --git a/core/src/components/virtual-scroll/test/basic.html b/core/src/components/virtual-scroll/test/basic.html index b45d84f683..407fe42b6a 100644 --- a/core/src/components/virtual-scroll/test/basic.html +++ b/core/src/components/virtual-scroll/test/basic.html @@ -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); }; diff --git a/core/src/components/virtual-scroll/test/cards.html b/core/src/components/virtual-scroll/test/cards.html index 4e3dd11a0b..f426fa7735 100644 --- a/core/src/components/virtual-scroll/test/cards.html +++ b/core/src/components/virtual-scroll/test/cards.html @@ -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); }; diff --git a/core/src/components/virtual-scroll/virtual-scroll-utils.tsx b/core/src/components/virtual-scroll/virtual-scroll-utils.tsx index e706ac429b..f6e07a4fec 100644 --- a/core/src/components/virtual-scroll/virtual-scroll-utils.tsx +++ b/core/src/components/virtual-scroll/virtual-scroll-utils.tsx @@ -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); } diff --git a/core/src/components/virtual-scroll/virtual-scroll.tsx b/core/src/components/virtual-scroll/virtual-scroll.tsx index 3c5e18f380..7e3ef6da85 100644 --- a/core/src/components/virtual-scroll/virtual-scroll.tsx +++ b/core/src/components/virtual-scroll/virtual-scroll.tsx @@ -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; }); }