diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index 80b2da6a10..628ab95afe 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -3618,6 +3618,7 @@ declare global { itemRender?: ItemRenderFn; items?: any[]; nodeHeight?: NodeHeightFn; + renderer?: (item: any) => JSX.Element; } } } diff --git a/packages/core/src/components/virtual-scroll/readme.md b/packages/core/src/components/virtual-scroll/readme.md index fc1f8ad989..5b50924797 100644 --- a/packages/core/src/components/virtual-scroll/readme.md +++ b/packages/core/src/components/virtual-scroll/readme.md @@ -280,6 +280,11 @@ which is an expensive operation and should be avoided if possible. +#### renderer + + + + ## Attributes #### approx-footer-height @@ -379,6 +384,11 @@ which is an expensive operation and should be avoided if possible. +#### renderer + + + + ## Methods #### markDirty() diff --git a/packages/core/src/components/virtual-scroll/virtual-scroll-utils.tsx b/packages/core/src/components/virtual-scroll/virtual-scroll-utils.tsx index bdce24312f..e117e8da93 100644 --- a/packages/core/src/components/virtual-scroll/virtual-scroll-utils.tsx +++ b/packages/core/src/components/virtual-scroll/virtual-scroll-utils.tsx @@ -44,7 +44,7 @@ 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; -export type DomRenderFn = (dom: VirtualNode[], height: number) => void; +export type DomRenderFn = (dom: VirtualNode[]) => void; export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells: Cell[], range: Range) { // reset dom diff --git a/packages/core/src/components/virtual-scroll/virtual-scroll.tsx b/packages/core/src/components/virtual-scroll/virtual-scroll.tsx index 15144454be..6767a221b5 100644 --- a/packages/core/src/components/virtual-scroll/virtual-scroll.tsx +++ b/packages/core/src/components/virtual-scroll/virtual-scroll.tsx @@ -13,7 +13,7 @@ import { Cell, DomRenderFn, HeaderFn, ItemHeightFn, }) export class VirtualScroll { - private scrollEl: HTMLElement | null; + private scrollEl: HTMLIonScrollElement | null; private range: Range = {offset: 0, length: 0}; private timerUpdate: any; private heightIndex: Uint32Array; @@ -99,6 +99,7 @@ export class VirtualScroll { */ @Prop() items: any[]; + @Prop() renderer: (item: any) => JSX.Element; @Prop() nodeHeight: NodeHeightFn; @Prop() itemHeight: ItemHeightFn; @Prop() itemRender: ItemRenderFn; @@ -116,9 +117,11 @@ export class VirtualScroll { console.error('virtual-scroll must be used inside ion-scroll/ion-content'); return; } - this.calcDimensions(); - this.calcCells(); - this.updateState(); + this.scrollEl.componentOnReady().then(() => { + this.calcDimensions(); + this.calcCells(); + this.updateState(); + }); } componentDidUpdate() { @@ -251,12 +254,14 @@ export class VirtualScroll { // write DOM if (this.itemRender) { doRender(this.el, this.itemRender, this.virtualDom, this.updateCellHeight.bind(this)); - if (this.heightChanged) { - this.el.style.height = this.totalHeight + 'px'; - this.heightChanged = false; - } } else if (this.domRender) { - this.domRender(this.virtualDom, this.totalHeight); + this.domRender(this.virtualDom); + } else if (this.renderer) { + (this.el as any).forceUpdate(); + } + if (this.heightChanged) { + this.el.style.height = this.totalHeight + 'px'; + this.heightChanged = false; } } @@ -299,7 +304,6 @@ export class VirtualScroll { const shouldEnable = !!( this.scrollEl && this.cells && - (this.itemRender || this.domRender) && this.viewportHeight > 1 ); if (shouldEnable !== this.isEnabled) { @@ -362,4 +366,28 @@ export class VirtualScroll { this.enableListener(this, 'scroll', shouldListen, this.scrollEl); } } + + render() { + const renderer = this.renderer; + if (renderer) { + return this.virtualDom.map((dom) => { + const item = renderer(dom.cell.value) as any; + const classes = ['virtual-item']; + if (!item.vattrs) { + item.vattrs = {}; + } + item.vattrs.class += ' virtual-item'; + if (!dom.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)`; + return item; + }); + } + return undefined; + } }