Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/powerpoint/slideFilmstripController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class SlideFilmstripController {
private cancelIdleThumbnailFill: (() => void) | null = null;
private renderedThumbnailIndices = new Set<number>();
private readonly thumbnailFontSubstitutions = new Map<number, FontSubstitution[]>();
private cachedThumbnailElements: HTMLElement[] | null = null;
private thumbnailPointerDrag: {
fromIndex: number;
pointerId: number;
Expand Down Expand Up @@ -358,6 +359,7 @@ export class SlideFilmstripController {
this.cancelIdleThumbnailFill = null;
this.renderedThumbnailIndices.clear();
this.thumbnailFontSubstitutions.clear();
this.cachedThumbnailElements = null;
thumbnailContainer.empty();

for (let index = 0; index < slideCount; index += 1) {
Expand Down Expand Up @@ -401,6 +403,7 @@ export class SlideFilmstripController {
private appendThumbnailShell(index: number, renderImmediately: boolean): void {
if (!this.host.thumbnailContainer) return;

this.cachedThumbnailElements = null;
const item = this.host.thumbnailContainer.createDiv({ cls: 'native-powerpoint-thumbnail' });
item.dataset.slideIndex = String(index);
if (index === this.host.currentSlide) item.addClass('active');
Expand Down Expand Up @@ -719,10 +722,29 @@ export class SlideFilmstripController {
return this.slideNavigationPromise;
}

private getThumbnailElements(): HTMLElement[] {
if (this.cachedThumbnailElements) {
return this.cachedThumbnailElements;
}
const container = this.host.thumbnailContainer;
if (!container) return [];

const nodes = container.querySelectorAll('.native-powerpoint-thumbnail');
const elements: HTMLElement[] = [];
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node instanceof HTMLElement) {
elements.push(node);
}
}
this.cachedThumbnailElements = elements;
return elements;
}

private updateThumbnailActiveState(): void {
if (!this.host.thumbnailContainer) return;

const items = this.host.thumbnailContainer.querySelectorAll('.native-powerpoint-thumbnail');
const items = this.getThumbnailElements();
items.forEach((item, index) => {
item.toggleClass('active', index === this.host.currentSlide);
item.toggleClass('is-selected', this.selectedSlideIndices.has(index));
Expand Down Expand Up @@ -1142,7 +1164,8 @@ export class SlideFilmstripController {
}

private applySlideSelectionClasses(): void {
this.host.thumbnailContainer?.querySelectorAll('.native-powerpoint-thumbnail').forEach((thumbnail, index) => {
const items = this.getThumbnailElements();
items.forEach((thumbnail, index) => {
thumbnail.classList.toggle('is-selected', this.selectedSlideIndices.has(index));
});
}
Expand Down
91 changes: 91 additions & 0 deletions tests/benchmark-thumbnail-update-mock.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { performance } from 'node:perf_hooks';

class MockElement {
constructor(className) {
this.className = className;
this.classList = new Set();
this.children = [];
}

toggleClass(cls, cond) {
if (cond) {
this.classList.add(cls);
} else {
this.classList.delete(cls);
}
}

querySelectorAll(selector) {
const cls = selector.replace('.', '');
const results = [];
const walk = (node) => {
if (node.className === cls) {
results.push(node);
}
for (const child of node.children) {
walk(child);
}
};
walk(this);
// querySelectorAll adds a forEach
results.forEach = Array.prototype.forEach;
return results;
}

appendChild(child) {
this.children.push(child);
}
}

const container = new MockElement('container');

// Create 500 items (typical large slide deck)
for (let i = 0; i < 500; i++) {
const div = new MockElement('native-powerpoint-thumbnail');
container.appendChild(div);
}

const host = {
thumbnailContainer: container,
currentSlide: 10
};
const selectedSlideIndices = new Set([10, 11, 12]);

function updateThumbnailActiveStateQSA() {
const items = host.thumbnailContainer.querySelectorAll('.native-powerpoint-thumbnail');
items.forEach((item, index) => {
item.toggleClass('active', index === host.currentSlide);
item.toggleClass('is-selected', selectedSlideIndices.has(index));
});
}

function updateThumbnailActiveStateChildren() {
const items = host.thumbnailContainer.children;
for (let index = 0; index < items.length; index++) {
const item = items[index];
item.toggleClass('active', index === host.currentSlide);
item.toggleClass('is-selected', selectedSlideIndices.has(index));
}
}

// Warmup
for (let i = 0; i < 1000; i++) {
updateThumbnailActiveStateQSA();
updateThumbnailActiveStateChildren();
}

let start, end;

start = performance.now();
for (let i = 0; i < 10000; i++) {
updateThumbnailActiveStateQSA();
}
end = performance.now();
console.log('querySelectorAll:', (end - start).toFixed(2), 'ms');

start = performance.now();
for (let i = 0; i < 10000; i++) {
updateThumbnailActiveStateChildren();
}
end = performance.now();
console.log('children:', (end - start).toFixed(2), 'ms');
Loading