pdf.js/src/display/editor/comment.js
Calixte Denizet 2a459857ce [Editor] Add editors with a comment in the sidebar
and add a button close to the editor.
Clicking on the button will display a popup with the comment but it's for a next patch.
2025-08-31 19:59:16 +02:00

183 lines
4.2 KiB
JavaScript

/* Copyright 2025 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 Comment {
#commentStandaloneButton = null;
#commentToolbarButton = null;
#commentWasFromKeyBoard = false;
#editor = null;
#initialText = null;
#text = null;
#date = null;
#deleted = false;
constructor(editor) {
this.#editor = editor;
}
renderForToolbar() {
const button = (this.#commentToolbarButton =
document.createElement("button"));
button.className = "comment";
return this.#render(button);
}
renderForStandalone() {
const button = (this.#commentStandaloneButton =
document.createElement("button"));
button.className = "annotationCommentButton";
const position = this.#editor.commentButtonPosition;
if (position) {
const { style } = button;
style.insetInlineEnd = `calc(${
100 *
(this.#editor._uiManager.direction === "ltr"
? 1 - position[0]
: position[0])
}% - var(--comment-button-dim))`;
style.top = `calc(${100 * position[1]}% - var(--comment-button-dim))`;
}
return this.#render(button);
}
#render(comment) {
if (!this.#editor._uiManager.hasCommentManager()) {
return null;
}
comment.tabIndex = "0";
comment.setAttribute("data-l10n-id", "pdfjs-editor-edit-comment-button");
const signal = this.#editor._uiManager._signal;
if (!(signal instanceof AbortSignal) || signal.aborted) {
return comment;
}
comment.addEventListener("contextmenu", noContextMenu, { signal });
comment.addEventListener("pointerdown", event => event.stopPropagation(), {
signal,
});
const onClick = event => {
event.preventDefault();
if (comment === this.#commentToolbarButton) {
this.edit();
} else {
this.#editor._uiManager.toggleComment(this.#editor);
}
};
comment.addEventListener("click", onClick, { capture: true, signal });
comment.addEventListener(
"keydown",
event => {
if (event.target === comment && event.key === "Enter") {
this.#commentWasFromKeyBoard = true;
onClick(event);
}
},
{ signal }
);
return comment;
}
edit() {
const { bottom, left, right } = this.#editor.getClientDimensions();
const position = { top: bottom };
if (this.#editor._uiManager.direction === "ltr") {
position.right = right;
} else {
position.left = left;
}
this.#editor._uiManager.editComment(this.#editor, position);
}
finish() {
if (!this.#commentToolbarButton) {
return;
}
this.#commentToolbarButton.focus({
focusVisible: this.#commentWasFromKeyBoard,
});
this.#commentWasFromKeyBoard = false;
}
isDeleted() {
return this.#deleted || this.#text === "";
}
hasBeenEdited() {
return this.isDeleted() || this.#text !== this.#initialText;
}
serialize() {
return this.data;
}
get data() {
return {
text: this.#text,
date: this.#date,
deleted: this.#deleted,
};
}
/**
* Set the comment data.
*/
set data(text) {
if (text === null) {
this.#text = "";
this.#deleted = true;
return;
}
this.#text = text;
this.#date = new Date();
this.#deleted = false;
}
setInitialText(text) {
this.#initialText = text;
this.data = text;
}
shown() {}
destroy() {
this.#commentToolbarButton?.remove();
this.#commentToolbarButton = null;
this.#commentStandaloneButton?.remove();
this.#commentStandaloneButton = null;
this.#text = "";
this.#date = null;
this.#editor = null;
this.#commentWasFromKeyBoard = false;
this.#deleted = false;
}
}
export { Comment };