From ae79bee2d2731e466c4ae314544a1a51ab852805 Mon Sep 17 00:00:00 2001 From: Ujjwal Sharma Date: Mon, 14 Oct 2024 16:34:47 +0200 Subject: [PATCH] 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. --- l10n/en-US/viewer.ftl | 6 + src/display/editor/tools.js | 12 +- web/app.js | 6 + web/dialog.css | 17 +-- web/message_bar.css | 250 ++++++++++++++++++++---------------- web/pdf_viewer.js | 6 +- web/toast_manager.js | 52 ++++++++ web/viewer.html | 12 ++ web/viewer.js | 1 + 9 files changed, 236 insertions(+), 126 deletions(-) create mode 100644 web/toast_manager.js diff --git a/l10n/en-US/viewer.ftl b/l10n/en-US/viewer.ftl index 080feb30d57be..7b31fbc5f02ea 100644 --- a/l10n/en-US/viewer.ftl +++ b/l10n/en-US/viewer.ftl @@ -493,3 +493,9 @@ 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 + +# Variables: +# $type (String) - the type of annotation that was just removed with an optional quantity. +pdfjs-editor-messagebar-annotation-removed-text = { $type } removed +pdfjs-editor-messagebar-annotation-undo-button = Undo +pdfjs-editor-messagebar-annotation-close-button = Close \ No newline at end of file diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index e266ab18972d8..2e82054ab8aaf 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -621,6 +621,8 @@ class AnnotationEditorUIManager { #viewer = null; + #toastManager; + static TRANSLATE_SMALL = 1; // page units. static TRANSLATE_BIG = 10; // page units. @@ -769,7 +771,8 @@ class AnnotationEditorUIManager { enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, - mlManager + mlManager, + toastManager ) { const signal = (this._signal = this.#abortController.signal); this.#container = container; @@ -804,6 +807,7 @@ class AnnotationEditorUIManager { rotation: 0, }; this.isShiftKeyDown = false; + this.#toastManager = toastManager; if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { Object.defineProperty(this, "reset", { @@ -2002,6 +2006,12 @@ class AnnotationEditorUIManager { const editors = [...this.#selectedEditors]; const cmd = () => { + this.#toastManager.show( + undo, + editors.length > 1 + ? `${editors.length} annotations` + : editors[0].constructor._type + ); for (const editor of editors) { editor.remove(); } diff --git a/web/app.js b/web/app.js index a6fa45458f1e2..0e87f70f498ae 100644 --- a/web/app.js +++ b/web/app.js @@ -87,6 +87,7 @@ import { PDFThumbnailViewer } from "web-pdf_thumbnail_viewer"; import { PDFViewer } from "./pdf_viewer.js"; import { Preferences } from "web-preferences"; import { SecondaryToolbar } from "web-secondary_toolbar"; +import { ToastManager } from "./toast_manager.js"; import { Toolbar } from "web-toolbar"; import { ViewHistory } from "./view_history.js"; @@ -450,6 +451,10 @@ const PDFViewerApplication = { ) : null; } + const toastManager = new ToastManager( + appConfig.annotationRemovedToast, + 10000 + ); const enableHWA = AppOptions.get("enableHWA"); const pdfViewer = new PDFViewer({ @@ -460,6 +465,7 @@ const PDFViewerApplication = { linkService: pdfLinkService, downloadManager, altTextManager, + toastManager, findController, scriptingManager: AppOptions.get("enableScripting") && pdfScriptingManager, diff --git a/web/dialog.css b/web/dialog.css index 1aadbb8877cd3..77c4b656e3c69 100644 --- a/web/dialog.css +++ b/web/dialog.css @@ -273,23 +273,14 @@ --message-bar-bg-color: #ffebcd; --message-bar-fg-color: #15141a; --message-bar-border-color: rgb(0 0 0 / 0.08); + --message-bar-icon: url(images/messageBar_warning.svg); --message-bar-icon-color: #cd411e; - --message-bar-close-button-border-radius: 4px; - --message-bar-close-button-border: none; - --message-bar-close-button-color: var(--text-primary-color); - --message-bar-close-button-hover-bg-color: rgb(21 20 26 / 0.14); - --message-bar-close-button-active-bg-color: rgb(21 20 26 / 0.21); - --message-bar-close-button-focus-bg-color: rgb(21 20 26 / 0.07); - --message-bar-close-button-color-hover: var(--text-primary-color); @media (prefers-color-scheme: dark) { --message-bar-bg-color: #5a3100; --message-bar-fg-color: #fbfbfe; --message-bar-border-color: rgb(255 255 255 / 0.08); --message-bar-icon-color: #e49c49; - --message-bar-close-button-hover-bg-color: rgb(251 251 254 / 0.14); - --message-bar-close-button-active-bg-color: rgb(251 251 254 / 0.21); - --message-bar-close-button-focus-bg-color: rgb(251 251 254 / 0.07); } @media screen and (forced-colors: active) { @@ -297,12 +288,6 @@ --message-bar-fg-color: CanvasText; --message-bar-border-color: CanvasText; --message-bar-icon-color: CanvasText; - --message-bar-close-button-color: ButtonText; - --message-bar-close-button-border: 1px solid ButtonText; - --message-bar-close-button-hover-bg-color: ButtonText; - --message-bar-close-button-active-bg-color: ButtonText; - --message-bar-close-button-focus-bg-color: ButtonText; - --message-bar-close-button-color-hover: HighlightText; } position: relative; diff --git a/web/message_bar.css b/web/message_bar.css index aefb6191e95a9..305a0ff01cbd4 100644 --- a/web/message_bar.css +++ b/web/message_bar.css @@ -1,119 +1,153 @@ .messageBar { - --message-bar-warning-icon: url(images/messageBar_warning.svg); - --closing-button-icon: url(images/messageBar_closingButton.svg); - - display: flex; - position: relative; - padding: 12px 8px 12px 0; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 8px; - align-self: stretch; - - border-radius: 4px; + --closing-button-icon: url(images/messageBar_closingButton.svg); + --message-bar-close-button-color: var(--text-primary-color); + --message-bar-close-button-color-hover: var(--text-primary-color); + --message-bar-close-button-border-radius: 4px; + --message-bar-close-button-border: none; + --message-bar-close-button-hover-bg-color: rgb(21 20 26 / 0.14); + --message-bar-close-button-active-bg-color: rgb(21 20 26 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(21 20 26 / 0.07); + + @media (prefers-color-scheme: dark) { + --message-bar-close-button-hover-bg-color: rgb(251 251 254 / 0.14); + --message-bar-close-button-active-bg-color: rgb(251 251 254 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(251 251 254 / 0.07); + } + + @media screen and (forced-colors: active) { + --message-bar-close-button-color: ButtonText; + --message-bar-close-button-border: 1px solid ButtonText; + --message-bar-close-button-hover-bg-color: ButtonText; + --message-bar-close-button-active-bg-color: ButtonText; + --message-bar-close-button-focus-bg-color: ButtonText; + --message-bar-close-button-color-hover: HighlightText; + } + + display: flex; + position: relative; + padding: 12px 8px 12px 0; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + align-self: stretch; + + border-radius: 4px; + + border: 1px solid var(--message-bar-border-color); + background: var(--message-bar-bg-color); + color: var(--message-bar-fg-color); + + > div { + display: flex; + padding-inline-start: 16px; + align-items: flex-start; + gap: 8px; + align-self: stretch; + padding-inline-end: 32px; + + &::before { + content: ""; + display: inline-block; + width: 16px; + height: 16px; + mask-image: var(--message-bar-icon); + mask-size: cover; + background-color: var(--message-bar-icon-color); + } + + > div { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + flex: 1 0 0; + + .title { + font-size: 13px; + font-weight: 590; + } + + .description { + font-size: 13px; + } + } + } + + .closeButton { + position: absolute; + width: 32px; + height: 32px; + inset-inline-end: 8px; + inset-block-start: 8px; + background: none; + border-radius: var(--message-bar-close-button-border-radius); + border: var(--message-bar-close-button-border); + + &::before { + content: ""; + display: inline-block; + width: 16px; + height: 16px; + mask-image: var(--closing-button-icon); + mask-size: cover; + background-color: var(--message-bar-close-button-color); + } + + &:is(:hover, :active, :focus)::before { + background-color: var(--message-bar-close-button-color-hover); + } + + &:hover { + background-color: var(--message-bar-close-button-hover-bg-color); + } + + &:active { + background-color: var(--message-bar-close-button-active-bg-color); + } + + &:focus { + background-color: var(--message-bar-close-button-focus-bg-color); + } + + > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; + } + } +} - border: 1px solid var(--message-bar-border-color); - background: var(--message-bar-bg-color); - color: var(--message-bar-fg-color); +#annotationRemovedToast { + --message-bar-icon: url(images/secondaryToolbarButton-documentProperties.svg); + --message-bar-icon-color: #0060DF; + --message-bar-bg-color: #deeafc; + --message-bar-fg-color: #15141a; + --text-primary-color: #15141a; + --message-bar-border-color: rgb(0 0 0 / 0.08); + + .undoButton { + background: rgba(21, 20, 26, 0.14); + border-radius: 4px; + font-weight: bold; + color: #15141a; + border: none; + padding: 8px 16px; + } > div { - display: flex; - padding-inline-start: 16px; - align-items: flex-start; - gap: 8px; - align-self: stretch; - - &::before { - content: ""; - display: inline-block; - width: 16px; - height: 16px; - mask-image: var(--message-bar-warning-icon); - mask-size: cover; - background-color: var(--message-bar-icon-color); - } - - > div { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - flex: 1 0 0; - - .title { - font-size: 13px; - font-weight: 590; - } - - .description { - font-size: 13px; - } - } + align-items: center; } .closeButton { - position: absolute; - width: 32px; - height: 32px; - inset-inline-end: 8px; - inset-block-start: 8px; - background: none; - border-radius: var(--message-bar-close-button-border-radius); - border: var(--message-bar-close-button-border); - - &::before { - content: ""; - display: inline-block; - width: 16px; - height: 16px; - mask-image: var(--closing-button-icon); - mask-size: cover; - background-color: var(--message-bar-close-button-color); - } - - &:is(:hover, :active, :focus)::before { - background-color: var(--message-bar-close-button-color-hover); - } - - &:hover { - background-color: var(--message-bar-close-button-hover-bg-color); - } - - &:active { - background-color: var(--message-bar-close-button-active-bg-color); - } - - &:focus { - background-color: var(--message-bar-close-button-focus-bg-color); - } - - > span { - display: inline-block; - width: 0; - height: 0; - overflow: hidden; - } + padding-top: 12px; } } -#undoAnnotationDeleteMessage { - --message-bar-bg-color: #DEEAFC; - --message-bar-fg-color: #15141a; - --message-bar-border-color: rgb(0 0 0 / 0.08); - --message-bar-icon-color: #cd411e; - --message-bar-close-button-border-radius: 4px; - --message-bar-close-button-border: none; - --message-bar-close-button-color: var(--text-primary-color); - --message-bar-close-button-hover-bg-color: rgb(21 20 26 / 0.14); - --message-bar-close-button-active-bg-color: rgb(21 20 26 / 0.21); - --message-bar-close-button-focus-bg-color: rgb(21 20 26 / 0.07); - --message-bar-close-button-color-hover: var(--text-primary-color); - - /* hacks */ - position: fixed; - bottom: 100px; - width: 400px; - left: 50%; - transform: translateX(-50%); +#toastContainer { + position: fixed; + bottom: 100px; + left: 50%; + transform: translateX(-50%); } diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 9f4147b949ed5..65b83f6a7559b 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -203,6 +203,8 @@ class PDFViewer { #altTextManager = null; + #toastManager = null; + #annotationEditorHighlightColors = null; #annotationEditorMode = AnnotationEditorType.NONE; @@ -280,6 +282,7 @@ class PDFViewer { this.downloadManager = options.downloadManager || null; this.findController = options.findController || null; this.#altTextManager = options.altTextManager || null; + this.#toastManager = options.toastManager || null; if (this.findController) { this.findController.onIsPageVisible = pageNumber => @@ -901,7 +904,8 @@ class PDFViewer { this.#enableHighlightFloatingButton, this.#enableUpdatedAddImage, this.#enableNewAltTextWhenAddingImage, - this.#mlManager + this.#mlManager, + this.#toastManager ); eventBus.dispatch("annotationeditoruimanager", { source: this, diff --git a/web/toast_manager.js b/web/toast_manager.js new file mode 100644 index 0000000000000..a256643d23b75 --- /dev/null +++ b/web/toast_manager.js @@ -0,0 +1,52 @@ +class ToastManager { + #toastElement; + + #duration; + + #timeoutID = null; + + #controller = null; + + constructor(elem, duration) { + this.#toastElement = elem; + this.#duration = duration; + } + + show(action, type) { + this.#toastElement.setAttribute("data-l10n-args", JSON.stringify({ type })); + this.#toastElement.removeAttribute("hidden"); + this.#timeoutID = setTimeout(() => { + this.#hide(); + }, this.#duration); + this.#controller = new AbortController(); + console.log(this.#toastElement, this.#toastElement.getElementById); + this.#toastElement + .querySelector("#annotationRemovedUndoButton") + .addEventListener( + "click", + () => { + action(); + this.#hide(); + }, + { signal: this.#controller.signal } + ); + this.#toastElement + .querySelector("#annotationRemovedCloseButton") + .addEventListener( + "click", + () => { + this.#hide(); + }, + { signal: this.#controller.signal } + ); + } + + #hide() { + this.#toastElement.setAttribute("hidden", ""); + clearTimeout(this.#timeoutID); + this.#controller.abort(); + this.#controller = null; + } +} + +export { ToastManager }; diff --git a/web/viewer.html b/web/viewer.html index 92345ad211d44..12191e17761e0 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -686,6 +686,18 @@ +
+ +
+
diff --git a/web/viewer.js b/web/viewer.js index a60142ae5d1b1..d1744b25f39ec 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -223,6 +223,7 @@ function getViewerConfiguration() { editorHighlightShowAll: document.getElementById("editorHighlightShowAll"), }, printContainer: document.getElementById("printContainer"), + annotationRemovedToast: document.getElementById("annotationRemovedToast"), }; }