diff --git a/test/integration/reorganize_pages_spec.mjs b/test/integration/reorganize_pages_spec.mjs index 0591fdce3..4ff59fe3a 100644 --- a/test/integration/reorganize_pages_spec.mjs +++ b/test/integration/reorganize_pages_spec.mjs @@ -1112,6 +1112,76 @@ describe("Reorganize Pages View", () => { }); }); + describe("Focus stays in sidebar after page operations (bug 2020731)", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait( + "page_with_number.pdf", + "#viewsManagerToggleButton", + "1", + null, + { enableSplitMerge: true } + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("should keep focus on a thumbnail after deleting pages", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForThumbnailVisible(page, 1); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(1)}) input` + ); + + const handlePagesEdited = await waitForPagesEdited(page); + await waitAndClick(page, "#viewsManagerStatusActionButton"); + await waitAndClick(page, "#viewsManagerStatusActionDelete"); + await awaitPromise(handlePagesEdited); + + await page.waitForSelector( + "#thumbnailsView .thumbnailImageContainer:focus", + { + visible: true, + } + ); + }) + ); + }); + + it("should keep focus on a thumbnail after pasting pages", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForThumbnailVisible(page, 1); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(1)}) input` + ); + + let handlePagesEdited = await waitForPagesEdited(page, "cut"); + await waitAndClick(page, "#viewsManagerStatusActionButton"); + await waitAndClick(page, "#viewsManagerStatusActionCut"); + await awaitPromise(handlePagesEdited); + + handlePagesEdited = await waitForPagesEdited(page); + await waitAndClick(page, `${getThumbnailSelector(1)}+button`); + await awaitPromise(handlePagesEdited); + + await page.waitForSelector( + "#thumbnailsView .thumbnailImageContainer:focus", + { + visible: true, + } + ); + }) + ); + }); + }); + describe("Extract some pages from a pdf", () => { let pages; diff --git a/web/pdf_thumbnail_viewer.js b/web/pdf_thumbnail_viewer.js index 252d4fa92..12e83934f 100644 --- a/web/pdf_thumbnail_viewer.js +++ b/web/pdf_thumbnail_viewer.js @@ -125,6 +125,8 @@ class PDFThumbnailViewer { #copiedPageNumbers = null; + #boundPastePages = this.#pastePages.bind(this); + #isCut = false; #isOneColumnView = false; @@ -252,6 +254,15 @@ class PDFThumbnailViewer { }); } + #resetCurrentThumbnail(newPageNumber) { + if (!this.pdfDocument) { + return; + } + const thumbnailView = this._thumbnails[this._currentPageNumber - 1]; + thumbnailView?.toggleCurrent(/* isCurrent = */ false); + this._currentPageNumber = newPageNumber; + } + scrollThumbnailIntoView(pageNumber) { if (!this.pdfDocument) { return; @@ -263,10 +274,8 @@ class PDFThumbnailViewer { return; } if (pageNumber !== this._currentPageNumber) { - const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1]; - prevThumbnailView?.toggleCurrent(/* isCurrent = */ false); + this.#resetCurrentThumbnail(pageNumber); thumbnailView.toggleCurrent(/* isCurrent = */ true); - this._currentPageNumber = pageNumber; } const { first, last, views } = this.#getVisibleThumbs(); @@ -640,10 +649,7 @@ class PDFThumbnailViewer { type: "move", }); - setTimeout(() => { - this.forceRendering(); - this.linkService.goToPage(currentPageNumber); - }, 0); + this.#updateCurrentPage(currentPageNumber); } if (!isNaN(this.#pageNumberToRemove)) { @@ -659,6 +665,17 @@ class PDFThumbnailViewer { this.#selectedPages.clear(); } + #updateCurrentPage(currentPageNumber) { + setTimeout(() => { + this.#resetCurrentThumbnail(0); + this.forceRendering(); + const newPageNumber = currentPageNumber || 1; + this.linkService.goToPage(newPageNumber); + const thumbnailView = this._thumbnails[newPageNumber - 1]; + thumbnailView.imageContainer.focus(); + }, 0); + } + #saveExtractedPages() { this.eventBus.dispatch("saveextractedpages", { source: this, @@ -688,7 +705,7 @@ class PDFThumbnailViewer { this.#clearSelection(); } for (const thumbnail of this._thumbnails) { - thumbnail.addPasteButton(this.#pastePages.bind(this)); + thumbnail.addPasteButton(this.#boundPastePages); } this.container.classList.add("pasteMode"); this.#toggleMenuEntries(false); @@ -728,10 +745,7 @@ class PDFThumbnailViewer { this.#isCut = false; this.#updateMenuEntries(); - setTimeout(() => { - this.forceRendering(); - this.linkService.goToPage(currentPageNumber || 1); - }, 0); + this.#updateCurrentPage(currentPageNumber); } #deletePages(type = "delete") { @@ -757,10 +771,7 @@ class PDFThumbnailViewer { type, }); - setTimeout(() => { - this.forceRendering(); - this.linkService.goToPage(currentPageNumber || 1); - }, 0); + this.#updateCurrentPage(currentPageNumber); } #updateMenuEntries() {