Skip to content

Commit 71a0677

Browse files
committed
[Editor] Add a toolbar to selected editors with a button to delete it (bug 1863763)
1 parent 42f3d57 commit 71a0677

File tree

7 files changed

+312
-1
lines changed

7 files changed

+312
-1
lines changed

src/display/editor/editor.js

+23
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
KeyboardManager,
2424
} from "./tools.js";
2525
import { FeatureTest, shadow, unreachable } from "../../shared/util.js";
26+
import { EditorToolbar } from "./toolbar.js";
2627
import { noContextMenu } from "../display_utils.js";
2728

2829
/**
@@ -62,6 +63,8 @@ class AnnotationEditor {
6263

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

66+
#editToolbar = null;
67+
6568
#focusedResizerName = "";
6669

6770
#hasBeenClicked = false;
@@ -1034,6 +1037,22 @@ class AnnotationEditor {
10341037
this.#altTextWasFromKeyBoard = false;
10351038
}
10361039

1040+
addEditToolbar() {
1041+
if (this.#editToolbar || this.#isInEditMode) {
1042+
return;
1043+
}
1044+
this.#editToolbar = new EditorToolbar(this, this._uiManager);
1045+
this.div.append(this.#editToolbar.render());
1046+
}
1047+
1048+
removeEditToolbar() {
1049+
if (!this.#editToolbar) {
1050+
return;
1051+
}
1052+
this.#editToolbar.remove();
1053+
this.#editToolbar = null;
1054+
}
1055+
10371056
getClientDimensions() {
10381057
return this.div.getBoundingClientRect();
10391058
}
@@ -1386,6 +1405,7 @@ class AnnotationEditor {
13861405
this.#moveInDOMTimeout = null;
13871406
}
13881407
this.#stopResizing();
1408+
this.removeEditToolbar();
13891409
}
13901410

13911411
/**
@@ -1543,6 +1563,8 @@ class AnnotationEditor {
15431563
select() {
15441564
this.makeResizable();
15451565
this.div?.classList.add("selectedEditor");
1566+
this.addEditToolbar();
1567+
this.#editToolbar?.show();
15461568
}
15471569

15481570
/**
@@ -1556,6 +1578,7 @@ class AnnotationEditor {
15561578
// go.
15571579
this._uiManager.currentLayer.div.focus();
15581580
}
1581+
this.#editToolbar?.hide();
15591582
}
15601583

15611584
/**

src/display/editor/ink.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ class InkEditor extends AnnotationEditor {
624624
this.div.classList.add("disabled");
625625

626626
this.#fitToContent(/* firstTime = */ true);
627-
this.makeResizable();
627+
this.select();
628628

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

src/display/editor/toolbar.js

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* Copyright 2023 Mozilla Foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import { noContextMenu } from "../display_utils.js";
17+
18+
class EditorToolbar {
19+
#div = null;
20+
21+
#editor;
22+
23+
#icons = null;
24+
25+
#uiManager;
26+
27+
constructor(editor, uiManager) {
28+
this.#editor = editor;
29+
this.#uiManager = uiManager;
30+
}
31+
32+
render() {
33+
const editToolbar = (this.#div = document.createElement("div"));
34+
editToolbar.className = "editToolbar";
35+
editToolbar.addEventListener("contextmenu", noContextMenu);
36+
editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown);
37+
38+
const icons = (this.#icons = document.createElement("div"));
39+
icons.className = "icons";
40+
editToolbar.append(icons);
41+
42+
this.#addBin();
43+
44+
return editToolbar;
45+
}
46+
47+
static #pointerDown(e) {
48+
e.stopPropagation();
49+
}
50+
51+
hide() {
52+
this.#div.classList.add("hidden");
53+
}
54+
55+
show() {
56+
this.#div.classList.remove("hidden");
57+
}
58+
59+
#addBin() {
60+
const bin = document.createElement("button");
61+
bin.className = "bin";
62+
bin.tabIndex = 0;
63+
bin.addEventListener("contextmenu", noContextMenu);
64+
bin.addEventListener(
65+
"click",
66+
() => {
67+
event.preventDefault();
68+
this.#uiManager.deleteEditor(this.#editor);
69+
},
70+
{ capture: true }
71+
);
72+
this.#icons.append(bin);
73+
}
74+
75+
remove() {
76+
this.#div.remove();
77+
}
78+
}
79+
80+
export { EditorToolbar };

src/display/editor/tools.js

+11
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,17 @@ class AnnotationEditorUIManager {
15611561
this.addCommands({ cmd, undo, mustExec: true });
15621562
}
15631563

1564+
deleteEditor(editor) {
1565+
const cmd = () => {
1566+
editor.remove();
1567+
};
1568+
const undo = () => {
1569+
this.#addEditorToLayer(editor);
1570+
};
1571+
1572+
this.addCommands({ cmd, undo, mustExec: true });
1573+
}
1574+
15641575
commitOrRemove() {
15651576
// An editor is being edited so just commit it.
15661577
this.#activeEditor?.commitOrRemove();

test/integration/freetext_editor_spec.mjs

+57
Original file line numberDiff line numberDiff line change
@@ -3053,4 +3053,61 @@ describe("FreeText Editor", () => {
30533053
);
30543054
});
30553055
});
3056+
3057+
describe("Delete a freetext in using the bin button", () => {
3058+
let pages;
3059+
3060+
beforeAll(async () => {
3061+
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
3062+
});
3063+
3064+
afterAll(async () => {
3065+
await closePages(pages);
3066+
});
3067+
3068+
it("must check that a freetext is deleted", async () => {
3069+
await Promise.all(
3070+
pages.map(async ([browserName, page]) => {
3071+
await switchToFreeText(page);
3072+
3073+
const rect = await page.$eval(".annotationEditorLayer", el => {
3074+
// With Chrome something is wrong when serializing a DomRect,
3075+
// hence we extract the values and just return them.
3076+
const { x, y } = el.getBoundingClientRect();
3077+
return { x, y };
3078+
});
3079+
3080+
const data = "Hello PDF.js World !!";
3081+
await page.mouse.click(rect.x + 100, rect.y + 100);
3082+
await page.waitForSelector(getEditorSelector(0), {
3083+
visible: true,
3084+
});
3085+
await page.type(`${getEditorSelector(0)} .internal`, data);
3086+
3087+
// Commit.
3088+
await page.keyboard.press("Escape");
3089+
await page.waitForSelector(
3090+
`${getEditorSelector(0)} .overlay.enabled`
3091+
);
3092+
3093+
// Delete it in using the button.
3094+
await page.click(`${getEditorSelector(0)} .editToolbar`);
3095+
await page.waitForFunction(
3096+
sel => !document.querySelector(sel),
3097+
{},
3098+
getEditorSelector(0)
3099+
);
3100+
await waitForStorageEntries(page, 0);
3101+
3102+
// Undo.
3103+
await kbUndo(page);
3104+
await waitForSerialized(page, 1);
3105+
3106+
await page.waitForSelector(getEditorSelector(0), {
3107+
visible: true,
3108+
});
3109+
})
3110+
);
3111+
});
3112+
});
30563113
});

0 commit comments

Comments
 (0)