mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-24 14:58:36 +08:00
feat(virtual-scroller): add <template> support
This commit is contained in:
@ -30,67 +30,36 @@
|
|||||||
<p>
|
<p>
|
||||||
<ion-button onclick="addItems()">Add Items</ion-button>
|
<ion-button onclick="addItems()">Add Items</ion-button>
|
||||||
</p>
|
</p>
|
||||||
<ion-virtual-scroll id="virtual"></ion-virtual-scroll>
|
|
||||||
|
<ion-virtual-scroll id="virtual">
|
||||||
|
<template>
|
||||||
|
<ion-item>
|
||||||
|
<ion-label></ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</template>
|
||||||
|
</ion-virtual-scroll>
|
||||||
|
|
||||||
<ion-infinite-scroll threshold="100px" id="infinite-scroll">
|
<ion-infinite-scroll threshold="100px" id="infinite-scroll">
|
||||||
<ion-infinite-scroll-content loadingSpinner="bubbles" loadingText="Loading more data...">
|
<ion-infinite-scroll-content loadingSpinner="bubbles" loadingText="Loading more data...">
|
||||||
</ion-infinite-scroll-content>
|
</ion-infinite-scroll-content>
|
||||||
</ion-infinite-scroll>
|
</ion-infinite-scroll>
|
||||||
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const virtual = document.getElementById('virtual');
|
|
||||||
const items = Array.from({ length: 100 }, (x, i) => i);
|
const items = Array.from({ length: 100 }, (x, i) => i);
|
||||||
|
const virtual = document.getElementById('virtual');
|
||||||
function addItems(append) {
|
virtual.itemHeight = () => 44;
|
||||||
if (!append) {
|
|
||||||
append = Array.from({ length: 10 }, (x, i) => "append" + i);
|
|
||||||
}
|
|
||||||
items.push(...append);
|
|
||||||
virtual.markDirtyTail(append.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual.itemHeight = () => 45;
|
|
||||||
virtual.headerFn = (item, index) => {
|
|
||||||
// if (index % 20 === 0) {
|
|
||||||
// return 'Header ' + index;
|
|
||||||
// }
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderItem(el, item) {
|
|
||||||
if (!el) {
|
|
||||||
el = document.createElement('ion-item');
|
|
||||||
const text = document.createTextNode(item);
|
|
||||||
el['$content'] = text;
|
|
||||||
el.appendChild(text);
|
|
||||||
} else {
|
|
||||||
el['$content'].nodeValue = item;
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderHeader(el, item) {
|
|
||||||
if (!el) {
|
|
||||||
el = document.createElement('ion-item-divider');
|
|
||||||
const text = document.createTextNode(item);
|
|
||||||
el['$content'] = text;
|
|
||||||
el.appendChild(text);
|
|
||||||
} else {
|
|
||||||
el['$content'].nodeValue = item;
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual.nodeRender = (el, cell) => {
|
|
||||||
if (cell.type === 0) return renderItem(el, cell.value);
|
|
||||||
return renderHeader(el, cell.value);
|
|
||||||
};
|
|
||||||
virtual.items = items;
|
virtual.items = items;
|
||||||
|
virtual.nodeRender = (el, cell) => {
|
||||||
|
if (cell.type === 0) {
|
||||||
|
renderItem(el, cell.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function renderItem(el, item) {
|
||||||
|
el.querySelector('ion-label').textContent = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const infiniteScroll = document.getElementById('infinite-scroll');
|
const infiniteScroll = document.getElementById('infinite-scroll');
|
||||||
@ -103,6 +72,14 @@
|
|||||||
console.log('Done');
|
console.log('Done');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function addItems(append) {
|
||||||
|
if (!append) {
|
||||||
|
append = Array.from({ length: 10 }, (x, i) => "append" + i);
|
||||||
|
}
|
||||||
|
items.push(...append);
|
||||||
|
virtual.markDirtyTail(append.length)
|
||||||
|
}
|
||||||
|
|
||||||
function getAsyncData() {
|
function getAsyncData() {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -20,11 +20,20 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-virtual-scroll id="virtual"></ion-virtual-scroll>
|
|
||||||
|
<ion-virtual-scroll id="virtual">
|
||||||
|
<template>
|
||||||
|
<ion-card>
|
||||||
|
<ion-card-header>
|
||||||
|
<ion-card-title>Card Header</ion-card-title>
|
||||||
|
</ion-card-header>
|
||||||
|
<ion-card-content class="content"></ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
</template>
|
||||||
|
</ion-virtual-scroll>
|
||||||
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -38,14 +47,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderItem(el, item) {
|
function renderItem(el, item) {
|
||||||
if (!el) {
|
el.querySelector('.content').textContent = item.content;
|
||||||
el = document.createElement('ion-card');
|
|
||||||
}
|
|
||||||
el.innerHTML = `
|
|
||||||
<ion-card-header>
|
|
||||||
<ion-card-title>Card Header</ion-card-title>
|
|
||||||
</ion-card-header>
|
|
||||||
<ion-card-content>${item.content}</ion-card-content>`;
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export function doRender(
|
|||||||
dom: VirtualNode[],
|
dom: VirtualNode[],
|
||||||
updateCellHeight: (cell: Cell, node: HTMLElement) => void
|
updateCellHeight: (cell: Cell, node: HTMLElement) => void
|
||||||
) {
|
) {
|
||||||
const children = el.children;
|
const children = Array.from(el.children).filter(n => n.tagName !== 'TEMPLATE');
|
||||||
const childrenNu = children.length;
|
const childrenNu = children.length;
|
||||||
let child: HTMLElement;
|
let child: HTMLElement;
|
||||||
for (let i = 0; i < dom.length; i++) {
|
for (let i = 0; i < dom.length; i++) {
|
||||||
@ -88,9 +88,10 @@ export function doRender(
|
|||||||
child = children[i] as HTMLElement;
|
child = children[i] as HTMLElement;
|
||||||
nodeRender(child, cell, i);
|
nodeRender(child, cell, i);
|
||||||
} else {
|
} else {
|
||||||
child = nodeRender(null, cell, i);
|
const newChild = createNode(el, cell.type);
|
||||||
|
child = nodeRender(newChild, cell, i) || newChild;
|
||||||
child.classList.add('virtual-item');
|
child.classList.add('virtual-item');
|
||||||
el.appendChild(child);
|
el.appendChild(child!);
|
||||||
}
|
}
|
||||||
(child as any)['$ionCell'] = cell;
|
(child as any)['$ionCell'] = cell;
|
||||||
} else {
|
} else {
|
||||||
@ -121,6 +122,22 @@ export function doRender(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNode(el: HTMLElement, type: CellType): HTMLElement | null {
|
||||||
|
const template = getTemplate(el, type);
|
||||||
|
if (template) {
|
||||||
|
return el.ownerDocument.importNode(template.content, true).children[0] as HTMLElement;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemplate(el: HTMLElement, type: CellType): HTMLTemplateElement | null {
|
||||||
|
switch (type) {
|
||||||
|
case CellType.Item: return el.querySelector('template:not([name])');
|
||||||
|
case CellType.Header: return el.querySelector('template[name=header]');
|
||||||
|
case CellType.Footer: return el.querySelector('template[name=footer]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getViewport(scrollTop: number, vierportHeight: number, margin: number): Viewport {
|
export function getViewport(scrollTop: number, vierportHeight: number, margin: number): Viewport {
|
||||||
return {
|
return {
|
||||||
top: Math.max(scrollTop - margin, 0),
|
top: Math.max(scrollTop - margin, 0),
|
||||||
|
Reference in New Issue
Block a user