list: keep element order in DOM

fixes #44193
This commit is contained in:
Joao Moreno 2018-02-22 16:22:02 +01:00
parent be7434b93e
commit 787378a250

View file

@ -155,7 +155,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const removeRange = intersect(previousRenderRange, deleteRange);
for (let i = removeRange.start; i < removeRange.end; i++) {
this.removeItemFromDOM(this.items[i]);
this.removeItemFromDOM(i);
}
const previousRestRange: IRange = { start: start + deleteCount, end: this.items.length };
@ -188,19 +188,20 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const removeRange = removeRanges[r];
for (let i = removeRange.start; i < removeRange.end; i++) {
this.removeItemFromDOM(this.items[i]);
this.removeItemFromDOM(i);
}
}
const unrenderedRestRanges = previousUnrenderedRestRanges.map(r => shift(r, delta));
const elementsRange = { start, end: start + elements.length };
const insertRanges = [elementsRange, ...unrenderedRestRanges].map(r => intersect(renderRange, r));
const beforeElement = this.getNextToLastElement(insertRanges);
for (let r = 0; r < insertRanges.length; r++) {
const insertRange = insertRanges[r];
for (let i = insertRange.start; i < insertRange.end; i++) {
this.insertItemInDOM(this.items[i], i);
this.insertItemInDOM(i, beforeElement);
}
}
@ -259,16 +260,17 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const rangesToInsert = relativeComplement(renderRange, previousRenderRange);
const rangesToRemove = relativeComplement(previousRenderRange, renderRange);
const beforeElement = this.getNextToLastElement(rangesToInsert);
for (const range of rangesToInsert) {
for (let i = range.start; i < range.end; i++) {
this.insertItemInDOM(this.items[i], i);
this.insertItemInDOM(i, beforeElement);
}
}
for (const range of rangesToRemove) {
for (let i = range.start; i < range.end; i++) {
this.removeItemFromDOM(this.items[i], );
this.removeItemFromDOM(i);
}
}
@ -286,13 +288,19 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
// DOM operations
private insertItemInDOM(item: IItem<T>, index: number): void {
private insertItemInDOM(index: number, beforeElement: HTMLElement | null): void {
const item = this.items[index];
if (!item.row) {
item.row = this.cache.alloc(item.templateId);
}
if (!item.row.domNode.parentElement) {
this.rowsContainer.appendChild(item.row.domNode);
if (beforeElement) {
this.rowsContainer.insertBefore(item.row.domNode, beforeElement);
} else {
this.rowsContainer.appendChild(item.row.domNode);
}
}
item.row.domNode.style.height = `${item.size}px`;
@ -310,7 +318,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
item.row.domNode.setAttribute('aria-posinset', `${index + 1}`);
}
private removeItemFromDOM(item: IItem<T>): void {
private removeItemFromDOM(index: number): void {
const item = this.items[index];
this.cache.release(item.row);
item.row = null;
}
@ -458,6 +467,26 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
};
}
private getNextToLastElement(ranges: IRange[]): HTMLElement | null {
const lastRange = ranges[ranges.length - 1];
if (!lastRange) {
return null;
}
const nextToLastItem = this.items[lastRange.end];
if (!nextToLastItem) {
return null;
}
if (!nextToLastItem.row) {
return null;
}
return nextToLastItem.row.domNode;
}
// Dispose
dispose() {