Skip to content

Commit 12066af

Browse files
committed
[Editor] Add a fake annotation (in the annotation layer) associated with an editor in order to be able to show the comment button (bug 1989420)
1 parent e3a5f61 commit 12066af

File tree

8 files changed

+292
-118
lines changed

8 files changed

+292
-118
lines changed

src/display/annotation_layer.js

Lines changed: 114 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,24 @@ class AnnotationElement {
284284
);
285285
}
286286

287+
set commentText(text) {
288+
const { data } = this;
289+
const popup = { deleted: !text, contents: text || "" };
290+
if (!this.annotationStorage.updateEditor(data.id, { popup })) {
291+
this.annotationStorage.setValue(`${AnnotationEditorPrefix}${data.id}`, {
292+
id: data.id,
293+
annotationType: data.annotationType,
294+
pageIndex: this.parent.page._pageIndex,
295+
popup,
296+
popupRef: data.popupRef,
297+
modificationDate: new Date(),
298+
});
299+
}
300+
if (!text) {
301+
this.removePopup();
302+
}
303+
}
304+
287305
removePopup() {
288306
(this.#popupElement?.popup || this.popup)?.remove();
289307
this.#popupElement = this.popup = null;
@@ -308,10 +326,8 @@ class AnnotationElement {
308326

309327
let popup = this.#popupElement?.popup || this.popup;
310328
if (!popup && newPopup?.text) {
311-
if (!this.parent._commentManager) {
312-
this._createPopup(newPopup);
313-
popup = this.#popupElement.popup;
314-
}
329+
this._createPopup(newPopup);
330+
popup = this.#popupElement.popup;
315331
}
316332
if (!popup) {
317333
return;
@@ -882,6 +898,56 @@ class AnnotationElement {
882898
}
883899
}
884900

901+
class EditorAnnotationElement extends AnnotationElement {
902+
constructor(parameters) {
903+
super(parameters, { isRenderable: true, ignoreBorder: true });
904+
this.editor = parameters.editor;
905+
}
906+
907+
render() {
908+
this.container.className = "editorAnnotation";
909+
return this.container;
910+
}
911+
912+
createOrUpdatePopup() {
913+
const { editor } = this;
914+
if (!editor.hasComment) {
915+
return;
916+
}
917+
this._createPopup(editor.comment);
918+
this.extraPopupElement.popup.renderCommentButton();
919+
}
920+
921+
get hasCommentButton() {
922+
return this.enableComment && this.editor.hasComment;
923+
}
924+
925+
get commentButtonPosition() {
926+
return this.editor.commentButtonPositionInPage;
927+
}
928+
929+
get commentText() {
930+
return this.editor.comment.text;
931+
}
932+
933+
set commentText(text) {
934+
this.editor.comment = text;
935+
if (!text) {
936+
this.removePopup();
937+
}
938+
}
939+
940+
get commentData() {
941+
return this.editor.getData();
942+
}
943+
944+
remove() {
945+
this.container.remove();
946+
this.container = null;
947+
this.removePopup();
948+
}
949+
}
950+
885951
class LinkAnnotationElement extends AnnotationElement {
886952
constructor(parameters, options = null) {
887953
super(parameters, {
@@ -2541,7 +2607,9 @@ class PopupElement {
25412607
}
25422608

25432609
#updateCommentButtonPosition() {
2544-
if (this.#firstElement.extraPopupElement) {
2610+
if (this.#firstElement.extraPopupElement && !this.#firstElement.editor) {
2611+
// If there's no editor associated with the annotation then the comment
2612+
// button position can't be changed.
25452613
return;
25462614
}
25472615
this.renderCommentButton();
@@ -2596,30 +2664,10 @@ class PopupElement {
25962664
}
25972665

25982666
set comment(text) {
2599-
const element = this.#firstElement;
2600-
const { data } = element;
26012667
if (text === this.comment) {
26022668
return;
26032669
}
2604-
const popup = { deleted: !text, contents: text || "" };
2605-
if (!element.annotationStorage.updateEditor(data.id, { popup })) {
2606-
element.annotationStorage.setValue(
2607-
`${AnnotationEditorPrefix}${data.id}`,
2608-
{
2609-
id: data.id,
2610-
annotationType: data.annotationType,
2611-
pageIndex: element.parent.page._pageIndex,
2612-
popup,
2613-
popupRef: data.popupRef,
2614-
modificationDate: new Date(),
2615-
}
2616-
);
2617-
}
2618-
2619-
this.#commentText = text;
2620-
if (!text) {
2621-
element.removePopup();
2622-
}
2670+
this.#firstElement.commentText = this.#commentText = text;
26232671
}
26242672

26252673
get parentBoundingClientRect() {
@@ -3681,10 +3729,14 @@ class AnnotationLayer {
36813729

36823730
#annotationCanvasMap = null;
36833731

3732+
#annotationStorage = null;
3733+
36843734
#editableAnnotations = new Map();
36853735

36863736
#structTreeLayer = null;
36873737

3738+
#linkService = null;
3739+
36883740
constructor({
36893741
div,
36903742
accessibilityManager,
@@ -3694,11 +3746,15 @@ class AnnotationLayer {
36943746
viewport,
36953747
structTreeLayer,
36963748
commentManager,
3749+
linkService,
3750+
annotationStorage,
36973751
}) {
36983752
this.div = div;
36993753
this.#accessibilityManager = accessibilityManager;
37003754
this.#annotationCanvasMap = annotationCanvasMap;
37013755
this.#structTreeLayer = structTreeLayer || null;
3756+
this.#linkService = linkService || null;
3757+
this.#annotationStorage = annotationStorage || new AnnotationStorage();
37023758
this.page = page;
37033759
this.viewport = viewport;
37043760
this.zIndex = 0;
@@ -3762,12 +3818,12 @@ class AnnotationLayer {
37623818
const elementParams = {
37633819
data: null,
37643820
layer,
3765-
linkService: params.linkService,
3821+
linkService: this.#linkService,
37663822
downloadManager: params.downloadManager,
37673823
imageResourcesPath: params.imageResourcesPath || "",
37683824
renderForms: params.renderForms !== false,
37693825
svgFactory: new DOMSVGFactory(),
3770-
annotationStorage: params.annotationStorage || new AnnotationStorage(),
3826+
annotationStorage: this.#annotationStorage,
37713827
enableComment: params.enableComment === true,
37723828
enableScripting: params.enableScripting === true,
37733829
hasJSActions: params.hasJSActions,
@@ -3832,11 +3888,11 @@ class AnnotationLayer {
38323888
* @param {IPDFLinkService} linkService
38333889
* @memberof AnnotationLayer
38343890
*/
3835-
async addLinkAnnotations(annotations, linkService) {
3891+
async addLinkAnnotations(annotations) {
38363892
const elementParams = {
38373893
data: null,
38383894
layer: this.div,
3839-
linkService,
3895+
linkService: this.#linkService,
38403896
svgFactory: new DOMSVGFactory(),
38413897
parent: this,
38423898
};
@@ -3919,6 +3975,34 @@ class AnnotationLayer {
39193975
return this.#editableAnnotations.get(id);
39203976
}
39213977

3978+
addFakeAnnotation(editor) {
3979+
const { div } = this;
3980+
const { id, rotation } = editor;
3981+
const element = new EditorAnnotationElement({
3982+
data: {
3983+
id,
3984+
rect: editor.getPDFRect(),
3985+
rotation,
3986+
},
3987+
editor,
3988+
layer: div,
3989+
parent: this,
3990+
enableComment: !!this._commentManager,
3991+
linkService: this.#linkService,
3992+
annotationStorage: this.#annotationStorage,
3993+
});
3994+
const htmlElement = element.render();
3995+
div.append(htmlElement);
3996+
this.#accessibilityManager?.moveElementInDOM(
3997+
div,
3998+
htmlElement,
3999+
htmlElement,
4000+
/* isRemovable = */ false
4001+
);
4002+
element.createOrUpdatePopup();
4003+
return element;
4004+
}
4005+
39224006
/**
39234007
* @private
39244008
*/

src/display/editor/annotation_editor_layer.js

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ class AnnotationEditorLayer {
170170
this.#cleanup();
171171
switch (mode) {
172172
case AnnotationEditorType.NONE:
173+
this.div.classList.toggle("nonEditing", true);
173174
this.disableTextSelection();
174175
this.togglePointerEvents(false);
175176
this.toggleAnnotationLayerPointerEvents(true);
@@ -193,6 +194,7 @@ class AnnotationEditorLayer {
193194

194195
this.toggleAnnotationLayerPointerEvents(false);
195196
const { classList } = this.div;
197+
classList.toggle("nonEditing", false);
196198
if (mode === AnnotationEditorType.POPUP) {
197199
classList.toggle("commentEditing", true);
198200
} else {
@@ -257,6 +259,7 @@ class AnnotationEditorLayer {
257259
this.#isEnabling = true;
258260
this.div.tabIndex = 0;
259261
this.togglePointerEvents(true);
262+
this.div.classList.toggle("nonEditing", false);
260263
this.#textLayerDblClickAC?.abort();
261264
this.#textLayerDblClickAC = null;
262265
const annotationElementIds = new Set();
@@ -269,27 +272,24 @@ class AnnotationEditorLayer {
269272
}
270273
}
271274

272-
if (!this.#annotationLayer) {
273-
this.#isEnabling = false;
274-
return;
275-
}
276-
277-
const editables = this.#annotationLayer.getEditableAnnotations();
278-
for (const editable of editables) {
279-
// The element must be hidden whatever its state is.
280-
editable.hide();
281-
if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) {
282-
continue;
283-
}
284-
if (annotationElementIds.has(editable.data.id)) {
285-
continue;
286-
}
287-
const editor = await this.deserialize(editable);
288-
if (!editor) {
289-
continue;
275+
const annotationLayer = this.#annotationLayer;
276+
if (annotationLayer) {
277+
for (const editable of annotationLayer.getEditableAnnotations()) {
278+
// The element must be hidden whatever its state is.
279+
editable.hide();
280+
if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) {
281+
continue;
282+
}
283+
if (annotationElementIds.has(editable.data.id)) {
284+
continue;
285+
}
286+
const editor = await this.deserialize(editable);
287+
if (!editor) {
288+
continue;
289+
}
290+
this.addOrRebuild(editor);
291+
editor.enableEditing();
290292
}
291-
this.addOrRebuild(editor);
292-
editor.enableEditing();
293293
}
294294
this.#isEnabling = false;
295295
this.#uiManager._eventBus.dispatch("editorsrendered", {
@@ -305,6 +305,7 @@ class AnnotationEditorLayer {
305305
this.#isDisabling = true;
306306
this.div.tabIndex = -1;
307307
this.togglePointerEvents(false);
308+
this.div.classList.toggle("nonEditing", true);
308309
if (this.#textLayer && !this.#textLayerDblClickAC) {
309310
this.#textLayerDblClickAC = new AbortController();
310311
const signal = this.#uiManager.combinedSignal(this.#textLayerDblClickAC);
@@ -351,26 +352,29 @@ class AnnotationEditorLayer {
351352
{ signal, capture: true }
352353
);
353354
}
354-
const changedAnnotations = new Map();
355-
const resetAnnotations = new Map();
356-
for (const editor of this.#allEditorsIterator) {
357-
editor.disableEditing();
358-
if (!editor.annotationElementId) {
359-
continue;
360-
}
361-
if (editor.serialize() !== null) {
362-
changedAnnotations.set(editor.annotationElementId, editor);
363-
continue;
364-
} else {
365-
resetAnnotations.set(editor.annotationElementId, editor);
355+
356+
const annotationLayer = this.#annotationLayer;
357+
if (annotationLayer) {
358+
const changedAnnotations = new Map();
359+
const resetAnnotations = new Map();
360+
for (const editor of this.#allEditorsIterator) {
361+
editor.disableEditing();
362+
if (!editor.annotationElementId) {
363+
editor.updateFakeAnnotationElement(annotationLayer);
364+
continue;
365+
}
366+
if (editor.serialize() !== null) {
367+
changedAnnotations.set(editor.annotationElementId, editor);
368+
continue;
369+
} else {
370+
resetAnnotations.set(editor.annotationElementId, editor);
371+
}
372+
this.getEditableAnnotation(editor.annotationElementId)?.show();
373+
editor.remove();
366374
}
367-
this.getEditableAnnotation(editor.annotationElementId)?.show();
368-
editor.remove();
369-
}
370375

371-
if (this.#annotationLayer) {
372376
// Show the annotations that were hidden in enable().
373-
const editables = this.#annotationLayer.getEditableAnnotations();
377+
const editables = annotationLayer.getEditableAnnotations();
374378
for (const editable of editables) {
375379
const { id } = editable.data;
376380
if (this.#uiManager.isDeletedAnnotationElement(id)) {
@@ -725,7 +729,7 @@ class AnnotationEditorLayer {
725729
/**
726730
* Create a new editor
727731
* @param {Object} data
728-
* @returns {AnnotationEditor | null}
732+
* @returns {Promise<AnnotationEditor | null>}
729733
*/
730734
async deserialize(data) {
731735
return (

0 commit comments

Comments
 (0)