mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-04-09 23:04:02 +02:00
Merge pull request #20850 from calixteman/bug2021828
Don't let the user delete/cut all the pages (bug 2021828)
This commit is contained in:
commit
fbfccebb81
@ -967,6 +967,42 @@ describe("Reorganize Pages View", () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should disable delete and cut entries when all pages are selected", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await waitForThumbnailVisible(page, 1);
|
||||
|
||||
// Select all pages.
|
||||
const totalPages = await page.evaluate(
|
||||
() =>
|
||||
document.querySelectorAll("#thumbnailsView .thumbnail input")
|
||||
.length
|
||||
);
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
await waitAndClick(
|
||||
page,
|
||||
`.thumbnail:has(${getThumbnailSelector(i)}) input`
|
||||
);
|
||||
}
|
||||
|
||||
await waitAndClick(page, "#viewsManagerStatusActionButton");
|
||||
|
||||
await page.waitForSelector(
|
||||
"#viewsManagerStatusActionDelete:disabled"
|
||||
);
|
||||
await page.waitForSelector("#viewsManagerStatusActionCut:disabled");
|
||||
await page.waitForSelector(
|
||||
"#viewsManagerStatusActionCopy:not(:disabled)"
|
||||
);
|
||||
await page.waitForSelector(
|
||||
"#viewsManagerStatusActionSaveAs:not(:disabled)"
|
||||
);
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Keyboard shortcuts for cut and copy (bug 2018139)", () => {
|
||||
@ -1746,4 +1782,148 @@ describe("Reorganize Pages View", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Context menu triggers editingstateschanged event (bug 2021828)", () => {
|
||||
let pages;
|
||||
|
||||
beforeEach(async () => {
|
||||
pages = await loadAndWait(
|
||||
"page_with_number.pdf",
|
||||
"#viewsManagerToggleButton",
|
||||
"1",
|
||||
null,
|
||||
{ enableSplitMerge: true }
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await closePages(pages);
|
||||
});
|
||||
|
||||
function getContextMenuPromise(page) {
|
||||
return createPromise(page, resolve => {
|
||||
window.addEventListener(
|
||||
"contextmenu",
|
||||
e => {
|
||||
e.preventDefault();
|
||||
resolve();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
it("should dispatch editingstateschanged with correct payload on right-click with no selection", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await waitForThumbnailVisible(page, 1);
|
||||
|
||||
const handleEditingStatesChanged = await createPromise(
|
||||
page,
|
||||
resolve => {
|
||||
window.PDFViewerApplication.eventBus.on(
|
||||
"editingstateschanged",
|
||||
({ details }) => resolve(details),
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const contextMenuPromise = await getContextMenuPromise(page);
|
||||
await page.click(getThumbnailSelector(1), { button: "right" });
|
||||
await awaitPromise(contextMenuPromise);
|
||||
|
||||
const details = await awaitPromise(handleEditingStatesChanged);
|
||||
expect(details.thumbnailId).withContext(`In ${browserName}`).toBe(1);
|
||||
expect(details.hasSelectedPages)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toBeFalse();
|
||||
expect(details.canDeletePages)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toBeFalse();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should dispatch editingstateschanged with correct payload on right-click with some pages selected", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await waitForThumbnailVisible(page, 1);
|
||||
await waitAndClick(
|
||||
page,
|
||||
`.thumbnail:has(${getThumbnailSelector(1)}) input`
|
||||
);
|
||||
|
||||
const handleEditingStatesChanged = await createPromise(
|
||||
page,
|
||||
resolve => {
|
||||
window.PDFViewerApplication.eventBus.on(
|
||||
"editingstateschanged",
|
||||
({ details }) => resolve(details),
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const contextMenuPromise = await getContextMenuPromise(page);
|
||||
await page.click(getThumbnailSelector(1), { button: "right" });
|
||||
await awaitPromise(contextMenuPromise);
|
||||
|
||||
const details = await awaitPromise(handleEditingStatesChanged);
|
||||
expect(details.thumbnailId).withContext(`In ${browserName}`).toBe(1);
|
||||
expect(details.hasSelectedPages)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toBeTrue();
|
||||
expect(details.canDeletePages)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toBeTrue();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should dispatch editingstateschanged with canDeletePages false when all pages are selected", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await waitForThumbnailVisible(page, 1);
|
||||
|
||||
// Select all 17 pages.
|
||||
const totalPages = await page.evaluate(
|
||||
() =>
|
||||
document.querySelectorAll("#thumbnailsView .thumbnail input")
|
||||
.length
|
||||
);
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
await waitAndClick(
|
||||
page,
|
||||
`.thumbnail:has(${getThumbnailSelector(i)}) input`
|
||||
);
|
||||
}
|
||||
|
||||
const handleEditingStatesChanged = await createPromise(
|
||||
page,
|
||||
resolve => {
|
||||
window.PDFViewerApplication.eventBus.on(
|
||||
"editingstateschanged",
|
||||
({ details }) => resolve(details),
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const contextMenuPromise = await getContextMenuPromise(page);
|
||||
await page.click(getThumbnailSelector(1), { button: "right" });
|
||||
await awaitPromise(contextMenuPromise);
|
||||
|
||||
const details = await awaitPromise(handleEditingStatesChanged);
|
||||
expect(details.thumbnailId).withContext(`In ${browserName}`).toBe(1);
|
||||
expect(details.hasSelectedPages)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toBeTrue();
|
||||
expect(details.canDeletePages)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toBeFalse();
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -141,10 +141,6 @@ class PDFThumbnailViewer {
|
||||
|
||||
#scrollableContainerHeight = 0;
|
||||
|
||||
#previousStates = {
|
||||
hasSelectedPages: false,
|
||||
};
|
||||
|
||||
#statusLabel = null;
|
||||
|
||||
#statusBar = null;
|
||||
@ -236,6 +232,29 @@ class PDFThumbnailViewer {
|
||||
}
|
||||
});
|
||||
|
||||
this.container.addEventListener(
|
||||
"contextmenu",
|
||||
e => {
|
||||
this.eventBus.dispatch("editingstateschanged", {
|
||||
source: this,
|
||||
details: {
|
||||
thumbnailId:
|
||||
parseInt(
|
||||
e.target
|
||||
.closest(".thumbnailImageContainer")
|
||||
?.parentElement.getAttribute("page-number")
|
||||
) ?? -1,
|
||||
hasSelectedPages: !!this.#selectedPages?.size,
|
||||
canDeletePages: this.#canDelete(),
|
||||
},
|
||||
});
|
||||
},
|
||||
{
|
||||
signal: abortSignal,
|
||||
passive: true,
|
||||
}
|
||||
);
|
||||
|
||||
this.#undoButton?.addEventListener("click", this.#undo.bind(this));
|
||||
this.#undoCloseButton?.addEventListener(
|
||||
"click",
|
||||
@ -254,24 +273,6 @@ class PDFThumbnailViewer {
|
||||
this.#addEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the different possible states of this manager, e.g. is there
|
||||
* something to copy, paste, ...
|
||||
* @param {Object} details
|
||||
*/
|
||||
#dispatchUpdateStates(details) {
|
||||
const hasChanged = Object.entries(details).some(
|
||||
([key, value]) => this.#previousStates[key] !== value
|
||||
);
|
||||
|
||||
if (hasChanged) {
|
||||
this.eventBus.dispatch("editingstateschanged", {
|
||||
source: this,
|
||||
details: Object.assign(this.#previousStates, details),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#scrollUpdated() {
|
||||
this.renderingQueue.renderHighestPriority();
|
||||
}
|
||||
@ -759,6 +760,11 @@ class PDFThumbnailViewer {
|
||||
});
|
||||
}
|
||||
|
||||
#canDelete() {
|
||||
const size = this.#selectedPages?.size || 0;
|
||||
return size > 0 && size < this._thumbnails.length;
|
||||
}
|
||||
|
||||
#togglePasteMode(enable) {
|
||||
this.#isInPasteMode = enable;
|
||||
if (enable) {
|
||||
@ -808,6 +814,10 @@ class PDFThumbnailViewer {
|
||||
}
|
||||
|
||||
#cutPages() {
|
||||
if (!this.#canDelete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#isCut = true;
|
||||
this.#copyPages(false);
|
||||
this.#deletePages(/* type = */ "cut");
|
||||
@ -839,10 +849,11 @@ class PDFThumbnailViewer {
|
||||
}
|
||||
|
||||
#deletePages(type = "delete") {
|
||||
const selectedPages = this.#selectedPages;
|
||||
if (selectedPages.size === 0) {
|
||||
if (!this.#canDelete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPages = this.#selectedPages;
|
||||
if (type === "delete") {
|
||||
this.#updateStatus("delete");
|
||||
}
|
||||
@ -868,14 +879,10 @@ class PDFThumbnailViewer {
|
||||
}
|
||||
|
||||
#updateMenuEntries() {
|
||||
this.#manageSaveAsButton.disabled =
|
||||
this.#manageDeleteButton.disabled =
|
||||
this.#manageCopyButton.disabled =
|
||||
this.#manageCutButton.disabled =
|
||||
!this.#selectedPages?.size;
|
||||
this.#dispatchUpdateStates({
|
||||
hasSelectedPages: !!this.#selectedPages?.size,
|
||||
});
|
||||
const size = this.#selectedPages?.size || 0;
|
||||
this.#manageSaveAsButton.disabled = this.#manageCopyButton.disabled = !size;
|
||||
this.#manageDeleteButton.disabled = this.#manageCutButton.disabled =
|
||||
!this.#canDelete();
|
||||
}
|
||||
|
||||
#toggleMenuEntries(enable) {
|
||||
@ -884,9 +891,6 @@ class PDFThumbnailViewer {
|
||||
this.#manageCopyButton.disabled =
|
||||
this.#manageCutButton.disabled =
|
||||
!enable;
|
||||
this.#dispatchUpdateStates({
|
||||
hasSelectedPages: false,
|
||||
});
|
||||
}
|
||||
|
||||
#updateStatus(type) {
|
||||
@ -1102,16 +1106,6 @@ class PDFThumbnailViewer {
|
||||
this.#computeThumbnailsPosition();
|
||||
}
|
||||
});
|
||||
this.container.addEventListener("focusout", () => {
|
||||
this.#dispatchUpdateStates({
|
||||
hasSelectedPages: false,
|
||||
});
|
||||
});
|
||||
this.container.addEventListener("focusin", () => {
|
||||
this.#dispatchUpdateStates({
|
||||
hasSelectedPages: !!this.#selectedPages?.size,
|
||||
});
|
||||
});
|
||||
this.container.addEventListener("keydown", e => {
|
||||
const { target } = e;
|
||||
const isCheckbox =
|
||||
@ -1218,6 +1212,7 @@ class PDFThumbnailViewer {
|
||||
if (
|
||||
e.button !== 0 || // Skip right click.
|
||||
this.#isInPasteMode ||
|
||||
this._thumbnails.length === 1 ||
|
||||
!isNaN(this.#lastDraggedOverIndex) ||
|
||||
!draggedImage.classList.contains("thumbnailImageContainer")
|
||||
) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user