1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-19 06:38:07 +02:00

Pop open a message when user deletes an annotation

When a user deletes any number of annotations, they are notified of the action
by a popup message with an undo button. Besides that, this change reuses the
existing messageBar CSS class from the new alt-text dialog as much as possible.
This commit is contained in:
Ujjwal Sharma 2024-10-22 13:23:47 +05:30
parent 962eb6206a
commit dd82d78a2d
11 changed files with 321 additions and 3 deletions

View file

@ -1148,6 +1148,7 @@ function buildComponents(defines, dir) {
"web/images/messageBar_*.svg",
"web/images/toolbarButton-{editorHighlight,menuArrow}.svg",
"web/images/cursor-*.svg",
"web/images/secondaryToolbarButton-documentProperties.svg",
];
return ordered([

View file

@ -503,3 +503,24 @@ pdfjs-editor-alt-text-settings-editor-title = Alt text editor
pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image
pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.
pdfjs-editor-alt-text-settings-close-button = Close
## "Annotations removed" bar
pdfjs-editor-undo-bar-message-highlight = Highlight removed
pdfjs-editor-undo-bar-message-freetext = Text removed
pdfjs-editor-undo-bar-message-ink = Drawing removed
pdfjs-editor-undo-bar-message-stamp = Image removed
# Variables:
# $count (Number) - the number of removed annotations.
pdfjs-editor-undo-bar-message-multiple =
{ $count ->
[one] { $count } annotation removed
*[other] { $count } annotations removed
}
pdfjs-editor-undo-bar-undo-button =
.title = Undo
pdfjs-editor-undo-bar-undo-button-label = Undo
pdfjs-editor-undo-bar-close-button =
.title = Close
pdfjs-editor-undo-bar-close-button-label = Close

View file

@ -676,6 +676,7 @@ class DrawingEditor extends AnnotationEditor {
signal,
});
parent.toggleDrawing();
uiManager._editorUndoBar?.hide();
if (this._currentDraw) {
parent.drawLayer.updateProperties(

View file

@ -1142,6 +1142,8 @@ class AnnotationEditor {
bindEvents(this, this.div, ["pointerdown"]);
this._uiManager._editorUndoBar?.hide();
return this.div;
}

View file

@ -620,6 +620,8 @@ class AnnotationEditorUIManager {
#editorsToRescale = new Set();
_editorUndoBar = null;
#enableHighlightFloatingButton = false;
#enableUpdatedAddImage = false;
@ -829,7 +831,8 @@ class AnnotationEditorUIManager {
enableHighlightFloatingButton,
enableUpdatedAddImage,
enableNewAltTextWhenAddingImage,
mlManager
mlManager,
editorUndoBar
) {
const signal = (this._signal = this.#abortController.signal);
this.#container = container;
@ -864,6 +867,7 @@ class AnnotationEditorUIManager {
rotation: 0,
};
this.isShiftKeyDown = false;
this._editorUndoBar = editorUndoBar || null;
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
Object.defineProperty(this, "reset", {
@ -904,6 +908,7 @@ class AnnotationEditorUIManager {
clearTimeout(this.#translationTimeoutId);
this.#translationTimeoutId = null;
}
this._editorUndoBar?.destroy();
}
combinedSignal(ac) {
@ -1656,6 +1661,8 @@ class AnnotationEditorUIManager {
this.setEditingState(false);
this.#disableAll();
this._editorUndoBar?.hide();
this.#updateModeCapability.resolve();
return;
}
@ -2038,6 +2045,7 @@ class AnnotationEditorUIManager {
hasSomethingToRedo: true,
isEmpty: this.#isEmpty(),
});
this._editorUndoBar?.hide();
}
/**
@ -2099,6 +2107,10 @@ class AnnotationEditorUIManager {
? [drawingEditor]
: [...this.#selectedEditors];
const cmd = () => {
this._editorUndoBar?.show(
undo,
editors.length === 1 ? editors[0].editorType : editors.length
);
for (const editor of editors) {
editor.remove();
}

View file

@ -69,6 +69,7 @@ import { AltTextManager } from "web-alt_text_manager";
import { AnnotationEditorParams } from "web-annotation_editor_params";
import { CaretBrowsingMode } from "./caret_browsing.js";
import { DownloadManager } from "web-download_manager";
import { EditorUndoBar } from "./editor_undo_bar.js";
import { OverlayManager } from "./overlay_manager.js";
import { PasswordPrompt } from "./password_prompt.js";
import { PDFAttachmentViewer } from "web-pdf_attachment_viewer";
@ -192,6 +193,7 @@ const PDFViewerApplication = {
_isCtrlKeyDown: false,
_caretBrowsing: null,
_isScrolling: false,
editorUndoBar: null,
// Called once when the document is loaded.
async initialize(appConfig) {
@ -461,6 +463,10 @@ const PDFViewerApplication = {
: null;
}
if (appConfig.editorUndoBar) {
this.editorUndoBar = new EditorUndoBar(appConfig.editorUndoBar, eventBus);
}
const enableHWA = AppOptions.get("enableHWA");
const pdfViewer = new PDFViewer({
container,
@ -470,6 +476,7 @@ const PDFViewerApplication = {
linkService: pdfLinkService,
downloadManager,
altTextManager,
editorUndoBar: this.editorUndoBar,
findController,
scriptingManager:
AppOptions.get("enableScripting") && pdfScriptingManager,
@ -2732,7 +2739,7 @@ function onTouchEnd(evt) {
this._isPinching = false;
}
function onClick(evt) {
function closeSecondaryToolbar(evt) {
if (!this.secondaryToolbar?.isOpen) {
return;
}
@ -2749,6 +2756,20 @@ function onClick(evt) {
}
}
function closeEditorUndoBar(evt) {
if (!this.editorUndoBar?.isOpen) {
return;
}
if (this.appConfig.secondaryToolbar?.toolbar.contains(evt.target)) {
this.editorUndoBar.hide();
}
}
function onClick(evt) {
closeSecondaryToolbar.call(this, evt);
closeEditorUndoBar.call(this, evt);
}
function onKeyUp(evt) {
// evt.ctrlKey is false hence we use evt.key.
if (evt.key === "Control") {
@ -2759,6 +2780,20 @@ function onKeyUp(evt) {
function onKeyDown(evt) {
this._isCtrlKeyDown = evt.key === "Control";
if (
this.editorUndoBar?.isOpen &&
evt.keyCode !== 9 &&
evt.keyCode !== 16 &&
!(
(evt.keyCode === 13 || evt.keyCode === 32) &&
getActiveOrFocusedElement() === this.appConfig.editorUndoBar.undoButton
)
) {
// Hide undo bar on keypress except for Shift, Tab, Shift+Tab.
// Also avoid hiding if the undo button is triggered.
this.editorUndoBar.hide();
}
if (this.overlayManager.active) {
return;
}

128
web/editor_undo_bar.js Normal file
View file

@ -0,0 +1,128 @@
/* Copyright 2024 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 "pdfjs-lib";
class EditorUndoBar {
#closeButton = null;
#container;
#eventBus = null;
#focusTimeout = null;
#initController = null;
isOpen = false;
#message;
#showController = null;
#undoButton;
static #l10nMessages = Object.freeze({
highlight: "pdfjs-editor-undo-bar-message-highlight",
freetext: "pdfjs-editor-undo-bar-message-freetext",
stamp: "pdfjs-editor-undo-bar-message-stamp",
ink: "pdfjs-editor-undo-bar-message-ink",
_multiple: "pdfjs-editor-undo-bar-message-multiple",
});
constructor({ container, message, undoButton, closeButton }, eventBus) {
this.#container = container;
this.#message = message;
this.#undoButton = undoButton;
this.#closeButton = closeButton;
this.#eventBus = eventBus;
}
destroy() {
this.#initController?.abort();
this.#initController = null;
this.hide();
}
show(undoAction, messageData) {
if (!this.#initController) {
this.#initController = new AbortController();
const opts = { signal: this.#initController.signal };
const boundHide = this.hide.bind(this);
this.#container.addEventListener("contextmenu", noContextMenu, opts);
this.#closeButton.addEventListener("click", boundHide, opts);
this.#eventBus._on("beforeprint", boundHide, opts);
this.#eventBus._on("download", boundHide, opts);
}
this.hide();
if (typeof messageData === "string") {
this.#message.setAttribute(
"data-l10n-id",
EditorUndoBar.#l10nMessages[messageData]
);
} else {
this.#message.setAttribute(
"data-l10n-id",
EditorUndoBar.#l10nMessages._multiple
);
this.#message.setAttribute(
"data-l10n-args",
JSON.stringify({ count: messageData })
);
}
this.isOpen = true;
this.#container.hidden = false;
this.#showController = new AbortController();
this.#undoButton.addEventListener(
"click",
() => {
undoAction();
this.hide();
},
{ signal: this.#showController.signal }
);
// Without the setTimeout, VoiceOver will read out the document title
// instead of the popup label.
this.#focusTimeout = setTimeout(() => {
this.#container.focus();
this.#focusTimeout = null;
}, 100);
}
hide() {
if (!this.isOpen) {
return;
}
this.isOpen = false;
this.#container.hidden = true;
this.#showController?.abort();
this.#showController = null;
if (this.#focusTimeout) {
clearTimeout(this.#focusTimeout);
this.#focusTimeout = null;
}
}
}
export { EditorUndoBar };

View file

@ -125,3 +125,97 @@
}
}
}
#editorUndoBar {
--text-primary-color: #15141a;
--message-bar-icon: url(images/secondaryToolbarButton-documentProperties.svg);
--message-bar-icon-color: #0060df;
--message-bar-bg-color: #deeafc;
--message-bar-fg-color: var(--text-primary-color);
--message-bar-border-color: rgb(0 0 0 / 0.08);
--undo-button-bg-color: rgb(21 20 26 / 0.07);
--undo-button-bg-color-hover: rgb(21 20 26 / 0.14);
--undo-button-bg-color-active: rgb(21 20 26 / 0.21);
--undo-button-fg-color: var(--message-bar-fg-color);
--undo-button-fg-color-hover: var(--undo-button-fg-color);
--undo-button-fg-color-active: var(--undo-button-fg-color);
--focus-ring-color: #0060df;
--focus-ring-outline: 2px solid var(--focus-ring-color);
@media (prefers-color-scheme: dark) {
--text-primary-color: #fbfbfe;
--message-bar-icon-color: #73a7f3;
--message-bar-bg-color: #003070;
--message-bar-border-color: rgb(255 255 255 / 0.08);
--undo-button-bg-color: rgb(255 255 255 / 0.08);
--undo-button-bg-color-hover: rgb(255 255 255 / 0.14);
--undo-button-bg-color-active: rgb(255 255 255 / 0.21);
}
@media screen and (forced-colors: active) {
--text-primary-color: CanvasText;
--message-bar-icon-color: CanvasText;
--message-bar-bg-color: Canvas;
--message-bar-border-color: CanvasText;
--undo-button-bg-color: ButtonText;
--undo-button-bg-color-hover: SelectedItem;
--undo-button-bg-color-active: SelectedItem;
--undo-button-fg-color: ButtonFace;
--undo-button-fg-color-hover: SelectedItemText;
--undo-button-fg-color-active: SelectedItemText;
--focus-ring-color: CanvasText;
}
position: fixed;
top: 50px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
padding-block: 8px;
padding-inline: 16px 8px;
font: menu;
font-size: 15px;
cursor: default;
button {
cursor: pointer;
}
#editorUndoBarUndoButton {
border-radius: 4px;
font-weight: 590;
line-height: 19.5px;
color: var(--undo-button-fg-color);
border: none;
padding: 4px 16px;
margin-inline-start: 8px;
height: 32px;
background-color: var(--undo-button-bg-color);
&:hover {
background-color: var(--undo-button-bg-color-hover);
}
&:active {
background-color: var(--undo-button-bg-color-active);
}
}
> div {
align-items: center;
}
}

View file

@ -214,6 +214,8 @@ class PDFViewer {
#containerTopLeft = null;
#editorUndoBar = null;
#enableHWA = false;
#enableHighlightFloatingButton = false;
@ -281,6 +283,7 @@ class PDFViewer {
this.downloadManager = options.downloadManager || null;
this.findController = options.findController || null;
this.#altTextManager = options.altTextManager || null;
this.#editorUndoBar = options.editorUndoBar || null;
if (this.findController) {
this.findController.onIsPageVisible = pageNumber =>
@ -907,7 +910,8 @@ class PDFViewer {
this.#enableHighlightFloatingButton,
this.#enableUpdatedAddImage,
this.#enableNewAltTextWhenAddingImage,
this.#mlManager
this.#mlManager,
this.#editorUndoBar
);
eventBus.dispatch("annotationeditoruimanager", {
source: this,

View file

@ -688,6 +688,20 @@ See https://github.com/adobe-type-tools/cmap-resources
<!--#endif-->
</div> <!-- dialogContainer -->
<div id="editorUndoBar" class="messageBar" role="status" aria-labelledby="editorUndoBarMessage" tabindex="-1" hidden>
<div>
<div>
<span id="editorUndoBarMessage" class="description"></span>
</div>
<button id="editorUndoBarUndoButton" class="undoButton" type="button" tabindex="0" title="Undo" data-l10n-id="pdfjs-editor-undo-bar-undo-button">
<span data-l10n-id="pdfjs-editor-undo-bar-undo-button-label">Undo</span>
</button>
<button id="editorUndoBarCloseButton" class="closeButton" type="button" tabindex="0" title="Close" data-l10n-id="pdfjs-editor-undo-bar-close-button">
<span data-l10n-id="pdfjs-editor-undo-bar-close-button-label">Close</span>
</button>
</div>
</div> <!-- editorUndoBar -->
</div> <!-- outerContainer -->
<div id="printContainer"></div>
</body>

View file

@ -223,6 +223,12 @@ function getViewerConfiguration() {
editorHighlightShowAll: document.getElementById("editorHighlightShowAll"),
},
printContainer: document.getElementById("printContainer"),
editorUndoBar: {
container: document.getElementById("editorUndoBar"),
message: document.getElementById("editorUndoBarMessage"),
undoButton: document.getElementById("editorUndoBarUndoButton"),
closeButton: document.getElementById("editorUndoBarCloseButton"),
},
};
}