mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-07-30 19:07:22 +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)", () => {
|
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;
|
#scrollableContainerHeight = 0;
|
||||||
|
|
||||||
#previousStates = {
|
|
||||||
hasSelectedPages: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
#statusLabel = null;
|
#statusLabel = null;
|
||||||
|
|
||||||
#statusBar = 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.#undoButton?.addEventListener("click", this.#undo.bind(this));
|
||||||
this.#undoCloseButton?.addEventListener(
|
this.#undoCloseButton?.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
@ -254,24 +273,6 @@ class PDFThumbnailViewer {
|
|||||||
this.#addEventListeners();
|
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() {
|
#scrollUpdated() {
|
||||||
this.renderingQueue.renderHighestPriority();
|
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) {
|
#togglePasteMode(enable) {
|
||||||
this.#isInPasteMode = enable;
|
this.#isInPasteMode = enable;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
@ -808,6 +814,10 @@ class PDFThumbnailViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#cutPages() {
|
#cutPages() {
|
||||||
|
if (!this.#canDelete()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.#isCut = true;
|
this.#isCut = true;
|
||||||
this.#copyPages(false);
|
this.#copyPages(false);
|
||||||
this.#deletePages(/* type = */ "cut");
|
this.#deletePages(/* type = */ "cut");
|
||||||
@ -839,10 +849,11 @@ class PDFThumbnailViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#deletePages(type = "delete") {
|
#deletePages(type = "delete") {
|
||||||
const selectedPages = this.#selectedPages;
|
if (!this.#canDelete()) {
|
||||||
if (selectedPages.size === 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedPages = this.#selectedPages;
|
||||||
if (type === "delete") {
|
if (type === "delete") {
|
||||||
this.#updateStatus("delete");
|
this.#updateStatus("delete");
|
||||||
}
|
}
|
||||||
@ -868,14 +879,10 @@ class PDFThumbnailViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#updateMenuEntries() {
|
#updateMenuEntries() {
|
||||||
this.#manageSaveAsButton.disabled =
|
const size = this.#selectedPages?.size || 0;
|
||||||
this.#manageDeleteButton.disabled =
|
this.#manageSaveAsButton.disabled = this.#manageCopyButton.disabled = !size;
|
||||||
this.#manageCopyButton.disabled =
|
this.#manageDeleteButton.disabled = this.#manageCutButton.disabled =
|
||||||
this.#manageCutButton.disabled =
|
!this.#canDelete();
|
||||||
!this.#selectedPages?.size;
|
|
||||||
this.#dispatchUpdateStates({
|
|
||||||
hasSelectedPages: !!this.#selectedPages?.size,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#toggleMenuEntries(enable) {
|
#toggleMenuEntries(enable) {
|
||||||
@ -884,9 +891,6 @@ class PDFThumbnailViewer {
|
|||||||
this.#manageCopyButton.disabled =
|
this.#manageCopyButton.disabled =
|
||||||
this.#manageCutButton.disabled =
|
this.#manageCutButton.disabled =
|
||||||
!enable;
|
!enable;
|
||||||
this.#dispatchUpdateStates({
|
|
||||||
hasSelectedPages: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateStatus(type) {
|
#updateStatus(type) {
|
||||||
@ -1102,16 +1106,6 @@ class PDFThumbnailViewer {
|
|||||||
this.#computeThumbnailsPosition();
|
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 => {
|
this.container.addEventListener("keydown", e => {
|
||||||
const { target } = e;
|
const { target } = e;
|
||||||
const isCheckbox =
|
const isCheckbox =
|
||||||
@ -1218,6 +1212,7 @@ class PDFThumbnailViewer {
|
|||||||
if (
|
if (
|
||||||
e.button !== 0 || // Skip right click.
|
e.button !== 0 || // Skip right click.
|
||||||
this.#isInPasteMode ||
|
this.#isInPasteMode ||
|
||||||
|
this._thumbnails.length === 1 ||
|
||||||
!isNaN(this.#lastDraggedOverIndex) ||
|
!isNaN(this.#lastDraggedOverIndex) ||
|
||||||
!draggedImage.classList.contains("thumbnailImageContainer")
|
!draggedImage.classList.contains("thumbnailImageContainer")
|
||||||
) {
|
) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user