From 7b988fed3c194b1a933fb65cd8cb63a59879fe85 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Tue, 19 Sep 2023 23:20:41 +0200 Subject: [PATCH] [Editor] Add a tooltip showing the alt text when hovering the alt-text button (bug 1844952) --- l10n/en-US/viewer.properties | 5 +- src/display/editor/editor.js | 73 +++++++++++++++---------- web/annotation_editor_layer_builder.css | 51 +++++++++++++++-- web/l10n_utils.js | 3 +- 4 files changed, 93 insertions(+), 39 deletions(-) diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties index 482983332e8d94..1f5db00a34cbcf 100644 --- a/l10n/en-US/viewer.properties +++ b/l10n/en-US/viewer.properties @@ -265,9 +265,9 @@ editor_ink2_aria_label=Draw Editor editor_ink_canvas_aria_label=User-created image # Alt-text dialog -# LOCALIZATION NOTE (alt_text_button_label): Alternative text (alt text) helps +# LOCALIZATION NOTE (editor_alt_text_button_label): Alternative text (alt text) helps # when people can't see the image. -alt_text_button_label=Alt text +editor_alt_text_button_label=Alt text editor_alt_text_dialog_label=Choose an option editor_alt_text_dialog_description=Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. editor_alt_text_enter_description_label=Enter a description @@ -276,3 +276,4 @@ editor_alt_text_mark_decorative_label=Mark as decorative editor_alt_text_mark_decorative_description=This is best for images that aren’t informative, like borders or stylistic elements. editor_alt_text_cancel_button=Cancel editor_alt_text_save_button=Save +editor_alt_text_decorative_tooltip=Marked as decorative diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index c5825d1c9aef10..7ea180ad56a10b 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -40,7 +40,9 @@ class AnnotationEditor { #altTextButton = null; - #altTextAriaDescription = null; + #altTextTooltip = null; + + #altTextTooltipTimeout = null; #keepAspectRatio = false; @@ -142,7 +144,10 @@ class AnnotationEditor { */ static initialize(l10n, options = null) { AnnotationEditor._l10nPromise ||= new Map( - ["alt_text_button_label"].map(str => [str, l10n.get(str)]) + [ + "editor_alt_text_button_label", + "editor_alt_text_decorative_tooltip", + ].map(str => [str, l10n.get(str)]) ); if (options?.strings) { for (const str of options.strings) { @@ -818,9 +823,11 @@ class AnnotationEditor { } const altText = (this.#altTextButton = document.createElement("button")); altText.className = "altText"; - AnnotationEditor._l10nPromise.get("alt_text_button_label").then(msg => { - altText.textContent = msg; - }); + AnnotationEditor._l10nPromise + .get("editor_alt_text_button_label") + .then(msg => { + altText.textContent = msg; + }); altText.tabIndex = "0"; altText.addEventListener( "click", @@ -836,6 +843,18 @@ class AnnotationEditor { this._uiManager.editAltText(this); } }); + const DELAY_TO_SHOW_TOOLTIP = 500; + altText.addEventListener("mouseenter", () => { + this.#altTextTooltipTimeout = setTimeout(() => { + this.#altTextTooltipTimeout = null; + this.#altTextTooltip?.classList.add("show"); + }, DELAY_TO_SHOW_TOOLTIP); + }); + altText.addEventListener("mouseleave", () => { + clearTimeout(this.#altTextTooltipTimeout); + this.#altTextTooltipTimeout = null; + this.#altTextTooltip?.classList.remove("show"); + }); this.#setAltTextButtonState(); this.div.append(altText); if (!AnnotationEditor.SMALL_EDITOR_SIZE) { @@ -849,36 +868,30 @@ class AnnotationEditor { } } - #setAltTextButtonState() { + async #setAltTextButtonState() { const button = this.#altTextButton; - if (!button) { + if (!button || (!this.#altTextDecorative && !this.#altText)) { return; } - // TODO: remove the aria-describedby once the tooltip stuff is implemented: - // the tooltip willl contain a span with the description, hence we could use - // it. + let tooltip = this.#altTextTooltip; + if (!tooltip) { + this.#altTextTooltip = tooltip = document.createElement("span"); + tooltip.className = "tooltip"; + tooltip.setAttribute("role", "tooltip"); + const id = (tooltip.id = `alt-text-tooltip-${this.id}`); + button.append(tooltip); + button.setAttribute("aria-describedby", id); + } + button.classList.add("done"); if (this.#altTextDecorative) { - button.classList.add("done"); - button.title = ""; - if (this.#altTextAriaDescription) { - button.removeAttribute("aria-describedby"); - this.#altTextAriaDescription.remove(); - this.#altTextAriaDescription = null; - } - } else if (this.#altText) { - button.classList.add("done"); - button.title = this.#altText; - let description = this.#altTextAriaDescription; - if (!description) { - this.#altTextAriaDescription = description = - document.createElement("span"); - description.className = "description"; - const id = (description.id = `${this.id}-alt-text`); - button.append(description); - button.setAttribute("aria-describedby", id); - } - description.innerText = this.#altText; + tooltip.innerText = await AnnotationEditor._l10nPromise.get( + "editor_alt_text_decorative_tooltip" + ); + tooltip.classList.add("decorative"); + return; } + tooltip.innerText = this.#altText; + tooltip.classList.remove("decorative"); } getClientDimensions() { diff --git a/web/annotation_editor_layer_builder.css b/web/annotation_editor_layer_builder.css index 6f796b2435d07a..0798a14ee3cf11 100644 --- a/web/annotation_editor_layer_builder.css +++ b/web/annotation_editor_layer_builder.css @@ -529,13 +529,52 @@ mask-image: var(--alt-text-done-image); } - & .description { - position: absolute; - top: 0; - left: 0; + & .tooltip { display: none; - width: 0; - height: 0; + + &.decorative { + font-style: italic; + } + + &.show { + --alt-text-tooltip-bg: #f0f0f4; + --alt-text-tooltip-fg: #15141a; + --alt-text-tooltip-shadow: 0px 2px 6px 0px rgba(58, 57, 68, 0.2); + + @media (prefers-color-scheme: dark) { + --alt-text-tooltip-bg: #1c1b22; + --alt-text-tooltip-fg: #fbfbfe; + --alt-text-tooltip-shadow: 0px 2px 6px 0px #15141a; + } + + @media screen and (forced-colors: active) { + --alt-text-tooltip-bg: Canvas; + --alt-text-tooltip-fg: CanvasText; + --alt-text-tooltip-border: CanvasText; + --alt-text-tooltip-shadow: none; + } + + display: inline-flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: absolute; + top: calc(100% + 2px); + inset-inline-start: 0; + padding-block: 2px 3px; + padding-inline: 3px; + max-width: 300px; + width: max-content; + height: auto; + font-size: 12px; + + border: 0.5px solid var(--alt-text-tooltip-border); + background: var(--alt-text-tooltip-bg); + box-shadow: var(--alt-text-tooltip-shadow); + color: var(--alt-text-tooltip-fg); + + pointer-events: none; + } } } diff --git a/web/l10n_utils.js b/web/l10n_utils.js index cc8b7d5c27a72b..d83e94ff3a5b0c 100644 --- a/web/l10n_utils.js +++ b/web/l10n_utils.js @@ -82,7 +82,8 @@ const DEFAULT_L10N_STRINGS = { editor_free_text2_aria_label: "Text Editor", editor_ink2_aria_label: "Draw Editor", editor_ink_canvas_aria_label: "User-created image", - alt_text_button_label: "Alt text", + editor_alt_text_button_label: "Alt text", + editor_alt_text_decorative_tooltip: "Marked as decorative", }; if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { DEFAULT_L10N_STRINGS.print_progress_percent = "{{progress}}%";