Skip to content

Commit

Permalink
[Editor] Add a toolbar to selected editors with a button to delete it…
Browse files Browse the repository at this point in the history
… (bug 1863763)
  • Loading branch information
calixteman committed Nov 8, 2023
1 parent 42f3d57 commit c43c71b
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 1 deletion.
1 change: 1 addition & 0 deletions gulpfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,7 @@ function buildComponents(defines, dir) {
"web/images/annotation-*.svg",
"web/images/loading-icon.gif",
"web/images/altText_*.svg",
"web/images/editor-toolbar-*.svg",
];

return merge([
Expand Down
23 changes: 23 additions & 0 deletions src/display/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
KeyboardManager,
} from "./tools.js";
import { FeatureTest, shadow, unreachable } from "../../shared/util.js";
import { EditorToolbar } from "./toolbar.js";
import { noContextMenu } from "../display_utils.js";

/**
Expand Down Expand Up @@ -62,6 +63,8 @@ class AnnotationEditor {

#boundFocusout = this.focusout.bind(this);

#editToolbar = null;

#focusedResizerName = "";

#hasBeenClicked = false;
Expand Down Expand Up @@ -1034,6 +1037,22 @@ class AnnotationEditor {
this.#altTextWasFromKeyBoard = false;
}

addEditToolbar() {
if (this.#editToolbar || this.#isInEditMode) {
return;
}
this.#editToolbar = new EditorToolbar(this, this._uiManager);
this.div.append(this.#editToolbar.render());
}

removeEditToolbar() {
if (!this.#editToolbar) {
return;
}
this.#editToolbar.remove();
this.#editToolbar = null;
}

getClientDimensions() {
return this.div.getBoundingClientRect();
}
Expand Down Expand Up @@ -1386,6 +1405,7 @@ class AnnotationEditor {
this.#moveInDOMTimeout = null;
}
this.#stopResizing();
this.removeEditToolbar();
}

/**
Expand Down Expand Up @@ -1543,6 +1563,8 @@ class AnnotationEditor {
select() {
this.makeResizable();
this.div?.classList.add("selectedEditor");
this.addEditToolbar();
this.#editToolbar?.show();
}

/**
Expand All @@ -1556,6 +1578,7 @@ class AnnotationEditor {
// go.
this._uiManager.currentLayer.div.focus();
}
this.#editToolbar?.hide();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/display/editor/ink.js
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ class InkEditor extends AnnotationEditor {
this.div.classList.add("disabled");

this.#fitToContent(/* firstTime = */ true);
this.makeResizable();
this.select();

this.parent.addInkEditorIfNeeded(/* isCommitting = */ true);

Expand Down
80 changes: 80 additions & 0 deletions src/display/editor/toolbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* Copyright 2023 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { noContextMenu } from "../display_utils.js";

class EditorToolbar {
#div = null;

#editor;

#icons = null;

#uiManager;

constructor(editor, uiManager) {
this.#editor = editor;
this.#uiManager = uiManager;
}

render() {
const editToolbar = (this.#div = document.createElement("div"));
editToolbar.className = "editToolbar";
editToolbar.addEventListener("contextmenu", noContextMenu);
editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown);

const icons = (this.#icons = document.createElement("div"));
icons.className = "icons";
editToolbar.append(icons);

this.#addDeleteButton();

return editToolbar;
}

static #pointerDown(e) {
e.stopPropagation();
}

hide() {
this.#div.classList.add("hidden");
}

show() {
this.#div.classList.remove("hidden");
}

#addDeleteButton() {
const button = document.createElement("button");
button.className = "delete";
button.tabIndex = 0;
button.addEventListener("contextmenu", noContextMenu);
button.addEventListener(
"click",
() => {
event.preventDefault();
this.#uiManager.deleteEditor(this.#editor);
},
{ capture: true }
);
this.#icons.append(button);
}

remove() {
this.#div.remove();
}
}

export { EditorToolbar };
11 changes: 11 additions & 0 deletions src/display/editor/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,17 @@ class AnnotationEditorUIManager {
this.addCommands({ cmd, undo, mustExec: true });
}

deleteEditor(editor) {
const cmd = () => {
editor.remove();
};
const undo = () => {
this.#addEditorToLayer(editor);
};

this.addCommands({ cmd, undo, mustExec: true });
}

commitOrRemove() {
// An editor is being edited so just commit it.
this.#activeEditor?.commitOrRemove();
Expand Down
57 changes: 57 additions & 0 deletions test/integration/freetext_editor_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3053,4 +3053,61 @@ describe("FreeText Editor", () => {
);
});
});

describe("Delete a freetext in using the bin button", () => {
let pages;

beforeAll(async () => {
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
});

afterAll(async () => {
await closePages(pages);
});

it("must check that a freetext is deleted", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToFreeText(page);

const rect = await page.$eval(".annotationEditorLayer", el => {
// With Chrome something is wrong when serializing a DomRect,
// hence we extract the values and just return them.
const { x, y } = el.getBoundingClientRect();
return { x, y };
});

const data = "Hello PDF.js World !!";
await page.mouse.click(rect.x + 100, rect.y + 100);
await page.waitForSelector(getEditorSelector(0), {
visible: true,
});
await page.type(`${getEditorSelector(0)} .internal`, data);

// Commit.
await page.keyboard.press("Escape");
await page.waitForSelector(
`${getEditorSelector(0)} .overlay.enabled`
);

// Delete it in using the button.
await page.click(`${getEditorSelector(0)} button.delete`);
await page.waitForFunction(
sel => !document.querySelector(sel),
{},
getEditorSelector(0)
);
await waitForStorageEntries(page, 0);

// Undo.
await kbUndo(page);
await waitForSerialized(page, 1);

await page.waitForSelector(getEditorSelector(0), {
visible: true,
});
})
);
});
});
});
Loading

0 comments on commit c43c71b

Please sign in to comment.