Parcourir la source

refactor(ui): use native <dialog> element instead of custom modal implementation

This simplifies the client-side code and improves accessibility.
Julien Voisin il y a 5 mois
Parent
commit
06c2e50ffa

+ 51 - 51
internal/template/templates/common/layout.html

@@ -126,62 +126,62 @@
     <main id="main">
         {{template "content" .}}
     </main>
-    <template id="keyboard-shortcuts">
-        <div id="modal-left">
-            <button class="btn-close-modal" aria-label="Close">x</button>
-            <h3 tabindex="-1" id="dialog-title">{{ t "page.keyboard_shortcuts.title" }}</h3>
+    <dialog id="keyboard-shortcuts-modal" closedby="any">
+        <form method="dialog">
+            <button class="btn-close-modal" aria-label="Close" autofocus>x</button>
+        </form>
+        <h3 tabindex="-1" id="dialog-title">{{ t "page.keyboard_shortcuts.title" }}</h3>
 
-            <div class="keyboard-shortcuts">
-                <p>{{ t "page.keyboard_shortcuts.subtitle.sections" }}</p>
-                <ul>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_unread" }} = <strong>g + u</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_starred" }} = <strong>g + b</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_history" }} = <strong>g + h</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_feeds" }} = <strong>g + f</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_categories" }} = <strong>g + c</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_settings" }} = <strong>g + s</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.show_keyboard_shortcuts" }} = <strong>?</strong></li>
-                    <li>{{ t "menu.add_feed" }} = <strong>+</strong></li>
-                </ul>
+        <div class="keyboard-shortcuts">
+            <p>{{ t "page.keyboard_shortcuts.subtitle.sections" }}</p>
+            <ul>
+                <li>{{ t "page.keyboard_shortcuts.go_to_unread" }} = <strong>g + u</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_starred" }} = <strong>g + b</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_history" }} = <strong>g + h</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_feeds" }} = <strong>g + f</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_categories" }} = <strong>g + c</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_settings" }} = <strong>g + s</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.show_keyboard_shortcuts" }} = <strong>?</strong></li>
+                <li>{{ t "menu.add_feed" }} = <strong>+</strong></li>
+            </ul>
 
-                <p>{{ t "page.keyboard_shortcuts.subtitle.items" }}</p>
-                <ul>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_previous_item" }} = <strong>p</strong>, <strong>k</strong>, <strong>&#x23F4;</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_next_item" }} = <strong>n</strong>, <strong>j</strong>, <strong>&#x23F5;</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_feed" }} = <strong>F</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_top_item" }} = <strong>g + g</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_bottom_item" }} = <strong>G</strong></li>
-                </ul>
+            <p>{{ t "page.keyboard_shortcuts.subtitle.items" }}</p>
+            <ul>
+                <li>{{ t "page.keyboard_shortcuts.go_to_previous_item" }} = <strong>p</strong>, <strong>k</strong>, <strong>&#x23F4;</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_next_item" }} = <strong>n</strong>, <strong>j</strong>, <strong>&#x23F5;</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_feed" }} = <strong>F</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_top_item" }} = <strong>g + g</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_bottom_item" }} = <strong>G</strong></li>
+            </ul>
 
-                <p>{{ t "page.keyboard_shortcuts.subtitle.pages" }}</p>
-                <ul>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_previous_page" }} = <strong>h</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_next_page" }} = <strong>l</strong></li>
-                </ul>
+            <p>{{ t "page.keyboard_shortcuts.subtitle.pages" }}</p>
+            <ul>
+                <li>{{ t "page.keyboard_shortcuts.go_to_previous_page" }} = <strong>h</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_next_page" }} = <strong>l</strong></li>
+            </ul>
 
-                <p>{{ t "page.keyboard_shortcuts.subtitle.actions" }}</p>
-                <ul>
-                    <li>{{ t "page.keyboard_shortcuts.open_item" }} = <strong>o</strong>, <strong>Enter</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.open_original" }} = <strong>v</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.open_original_same_window" }} = <strong>V</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.open_comments" }} = <strong>c</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.open_comments_same_window" }} = <strong>C</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.toggle_read_status_next" }} = <strong>m</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.toggle_read_status_prev" }} = <strong>M</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.mark_page_as_read" }} = <strong>A</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.download_content" }} = <strong>d</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.toggle_star_status" }} = <strong>f</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.save_article" }} = <strong>s</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.toggle_entry_attachments" }} = <strong>a</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.scroll_item_to_top" }} = <strong>z + t</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.refresh_all_feeds" }} = <strong>R</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.remove_feed" }} = <strong>#</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.go_to_search" }} = <strong>/</strong></li>
-                    <li>{{ t "page.keyboard_shortcuts.close_modal" }} = <strong>Esc</strong></li>
-                </ul>
-            </div>
+            <p>{{ t "page.keyboard_shortcuts.subtitle.actions" }}</p>
+            <ul>
+                <li>{{ t "page.keyboard_shortcuts.open_item" }} = <strong>o</strong>, <strong>Enter</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.open_original" }} = <strong>v</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.open_original_same_window" }} = <strong>V</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.open_comments" }} = <strong>c</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.open_comments_same_window" }} = <strong>C</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.toggle_read_status_next" }} = <strong>m</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.toggle_read_status_prev" }} = <strong>M</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.mark_page_as_read" }} = <strong>A</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.download_content" }} = <strong>d</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.toggle_star_status" }} = <strong>f</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.save_article" }} = <strong>s</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.toggle_entry_attachments" }} = <strong>a</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.scroll_item_to_top" }} = <strong>z + t</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.refresh_all_feeds" }} = <strong>R</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.remove_feed" }} = <strong>#</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.go_to_search" }} = <strong>/</strong></li>
+                <li>{{ t "page.keyboard_shortcuts.close_modal" }} = <strong>Esc</strong></li>
+            </ul>
         </div>
-    </template>
+    </dialog>
 
     <template id="icon-read">{{ icon "read" }}</template>
     <template id="icon-unread">{{ icon "unread" }}</template>

+ 6 - 17
internal/ui/static/css/common.css

@@ -619,22 +619,11 @@ template {
     display: none;
 }
 
-#modal-left {
-    position: fixed;
-    top: 0;
-    left: 0;
-    bottom: 0;
-    width: 380px;
-    overflow: auto;
-    color: var(--modal-color);
-    background: var(--modal-background);
-    box-shadow: var(--modal-box-shadow);
-    padding: 30px 5px 5px;
-}
-
-#modal-left h3 {
-    font-weight: 400;
-    margin: 0;
+dialog {
+    max-height: none;
+    height: 100vh;
+    border: none;
+    padding: 1%;
 }
 
 .btn-close-modal {
@@ -1342,4 +1331,4 @@ footer .elevator {
 .pagination-top .elevator,
 .pagination-entry-top .elevator {
     display: none;
-}
+}

+ 0 - 4
internal/ui/static/css/dark.css

@@ -62,10 +62,6 @@
     --panel-border-color: #555;
     --panel-color: #9b9b9b;
 
-    --modal-background: #333;
-    --modal-color: #efefef;
-    --modal-box-shadow: 0 0 10px rgba(82, 168, 236, 0.6);
-
     --pagination-link-color: #aaa;
     --pagination-border-color: #333;
 

+ 0 - 4
internal/ui/static/css/light.css

@@ -62,10 +62,6 @@
     --panel-border-color: #ddd;
     --panel-color: #333;
 
-    --modal-background: #f0f0f0;
-    --modal-color: #333;
-    --modal-box-shadow: 2px 0 5px 0 #ccc;
-
     --pagination-link-color: #333;
     --pagination-border-color: #ddd;
 

+ 0 - 4
internal/ui/static/css/system.css

@@ -61,10 +61,6 @@
     --panel-border-color: #ddd;
     --panel-color: #333;
 
-    --modal-background: #f0f0f0;
-    --modal-color: #333;
-    --modal-box-shadow: 2px 0 5px 0 #ccc;
-
     --pagination-link-color: #333;
     --pagination-border-color: #ddd;
 

+ 1 - 3
internal/ui/static/js/app.js

@@ -569,8 +569,7 @@ function initializeFormHandlers() {
  * Show the keyboard shortcuts modal.
  */
 function showKeyboardShortcutsAction() {
-    const template = document.getElementById("keyboard-shortcuts");
-    KeyboardModalHandler.open(template.content, "dialog-title");
+    document.getElementById("keyboard-shortcuts-modal").showModal();
 }
 
 /**
@@ -1215,7 +1214,6 @@ function initializeKeyboardShortcuts() {
 
     // UI actions
     keyboardHandler.on("?", showKeyboardShortcutsAction);
-    keyboardHandler.on("Escape", () => KeyboardModalHandler.close());
     keyboardHandler.on("a", () => {
         const enclosureElement = document.querySelector('.entry-enclosures');
         if (enclosureElement) {

+ 0 - 55
internal/ui/static/js/modal_handler.js

@@ -1,55 +0,0 @@
-class KeyboardModalHandler {
-    static setupFocusTrap() {
-        const container = document.getElementById("modal-container");
-        if (container !== null) {
-            container.onkeydown = (e) => {
-                if (e.key === 'Tab') {
-                    // Since there is only one focusable button in the keyboard modal we always want to focus it with the tab key. This handles
-                    // the special case of having just one focusable element in a dialog/ where keyboard focus is placed on an element that is not in the
-                    // tab order.
-                    container.querySelectorAll('button')[0].focus();
-                    e.preventDefault();
-                }
-            };
-        }
-    }
-
-    static open(fragment, initialFocusElementId) {
-        if (document.getElementById("modal-container") !== null){
-            return;
-        }
-
-        this.activeElement = document.activeElement;
-
-        const container = document.createElement("div");
-        container.id = "modal-container";
-        container.setAttribute("role", "dialog");
-        container.appendChild(document.importNode(fragment, true));
-        document.body.appendChild(container);
-
-        const closeButton = document.querySelector("button.btn-close-modal");
-        if (closeButton !== null) {
-            closeButton.onclick = (event) => {
-                event.preventDefault();
-                KeyboardModalHandler.close();
-            };
-        }
-
-        const initialFocusElement = document.getElementById(initialFocusElementId);
-        if (initialFocusElement !== undefined) {
-            initialFocusElement.focus();
-        }
-
-        this.setupFocusTrap();
-    }
-
-    static close() {
-        const container = document.getElementById("modal-container");
-        if (container !== null) {
-            container.parentNode.removeChild(container);
-            if (this.activeElement !== undefined && this.activeElement !== null) {
-                this.activeElement.focus();
-            }
-        }
-    }
-}

+ 0 - 1
internal/ui/static/static.go

@@ -123,7 +123,6 @@ func GenerateJavascriptBundles(webauthnEnabled bool) error {
 		"app": {
 			"js/touch_handler.js",
 			"js/keyboard_handler.js",
-			"js/modal_handler.js",
 			"js/app.js",
 		},
 		"service-worker": {