diff --git a/internal/template/templates/common/feed_list.html b/internal/template/templates/common/feed_list.html index d6b0adc4..ee937949 100644 --- a/internal/template/templates/common/feed_list.html +++ b/internal/template/templates/common/feed_list.html @@ -59,27 +59,25 @@ aria-describedby="feed-title-{{ .ID }}">{{ icon "edit" }}{{ t "menu.edit_feed" }}
  • - {{ icon "delete" }}{{ t "action.remove" }} + data-url="{{ route "removeFeed" "feedID" .ID }}">{{ icon "delete" }}{{ t "action.remove" }}
  • {{ if .UnreadCount }}
  • - {{ icon "read" }}{{ t "menu.mark_all_as_read" }} + data-url="{{ route "markFeedAsRead" "feedID" .ID }}">{{ icon "read" }}{{ t "menu.mark_all_as_read" }}
  • {{ end }} diff --git a/internal/template/templates/common/feed_menu.html b/internal/template/templates/common/feed_menu.html index 546c44d9..e2ea22a7 100644 --- a/internal/template/templates/common/feed_menu.html +++ b/internal/template/templates/common/feed_menu.html @@ -1,19 +1,23 @@ {{ define "feed_menu" }} {{ end }} diff --git a/internal/template/templates/common/item_meta.html b/internal/template/templates/common/item_meta.html index 8d48c983..47c8f1ee 100644 --- a/internal/template/templates/common/item_meta.html +++ b/internal/template/templates/common/item_meta.html @@ -17,8 +17,7 @@ diff --git a/internal/template/templates/views/category_entries.html b/internal/template/templates/views/category_entries.html index 7ba7bedb..a610bf61 100644 --- a/internal/template/templates/views/category_entries.html +++ b/internal/template/templates/views/category_entries.html @@ -17,40 +17,47 @@ @@ -101,13 +108,14 @@ {{ if .entries }} {{ end }} diff --git a/internal/template/templates/views/category_feeds.html b/internal/template/templates/views/category_feeds.html index 9930081f..a49a3e4d 100644 --- a/internal/template/templates/views/category_feeds.html +++ b/internal/template/templates/views/category_feeds.html @@ -10,26 +10,39 @@ diff --git a/internal/template/templates/views/entry.html b/internal/template/templates/views/entry.html index ec0bd747..6a4ed9c5 100644 --- a/internal/template/templates/views/entry.html +++ b/internal/template/templates/views/entry.html @@ -10,8 +10,8 @@
    @@ -131,13 +135,14 @@ {{ if .entries }} {{ end }} diff --git a/internal/template/templates/views/history_entries.html b/internal/template/templates/views/history_entries.html index 8d6358ef..cf492828 100644 --- a/internal/template/templates/views/history_entries.html +++ b/internal/template/templates/views/history_entries.html @@ -11,18 +11,18 @@ diff --git a/internal/template/templates/views/login.html b/internal/template/templates/views/login.html index 153723b9..56b8ac74 100644 --- a/internal/template/templates/views/login.html +++ b/internal/template/templates/views/login.html @@ -43,6 +43,6 @@ {{ end }} {{ end }} diff --git a/internal/template/templates/views/shared_entries.html b/internal/template/templates/views/shared_entries.html index 9aa09c80..c4728864 100644 --- a/internal/template/templates/views/shared_entries.html +++ b/internal/template/templates/views/shared_entries.html @@ -11,17 +11,17 @@ diff --git a/internal/template/templates/views/unread_entries.html b/internal/template/templates/views/unread_entries.html index 72511adb..0253cd94 100644 --- a/internal/template/templates/views/unread_entries.html +++ b/internal/template/templates/views/unread_entries.html @@ -11,25 +11,25 @@ @@ -75,8 +75,8 @@ {{ if .entries }} {{ end }} diff --git a/internal/ui/static/css/common.css b/internal/ui/static/css/common.css index b15c0f51..0a3e4ef4 100644 --- a/internal/ui/static/css/common.css +++ b/internal/ui/static/css/common.css @@ -180,6 +180,26 @@ a:hover { padding-bottom: 2px; } +.page-header-action-form { + display: inline-flex; +} + +:is(.page-button, .page-link) { + color: var(--link-color); + border: none; + background-color: transparent; + font-size: 1rem; + cursor: pointer; + + &:is(hover, :focus) { + color: var(--link-hover-color); + } +} + +.page-button:active { + translate: 1px 1px; +} + /* Logo */ .logo { text-align: center; @@ -850,8 +870,7 @@ template { text-decoration: none; } -.item-meta a:hover, -.item-meta a:focus { +.item-meta :is(a:is(:focus, :hover), button:is(:focus, :hover)) { color: #333; } @@ -881,6 +900,23 @@ template { margin-right: 0; } +.item-meta-icons li > :is(a, button) { + color: #777; + text-decoration: none; + font-size: 0.8rem; + border: none; + background-color: transparent; + cursor: pointer; +} + +.item-meta-icons a span { + text-decoration: underline; +} + +.item-meta-icons button:active { + translate: 1px 1px; +} + .items { overflow-x: hidden; touch-action: pan-y; @@ -967,8 +1003,8 @@ article.category-has-unread { margin-bottom: 20px; } -.entry-actions a { - text-decoration: none; +.entry-actions a span { + text-decoration: underline; } .entry-actions li { @@ -1198,8 +1234,12 @@ details.entry-enclosures { color: #ed2d04; } -.confirm a { +.confirm button { color: #ed2d04; + border: none; + background-color: transparent; + cursor: pointer; + font-size: inherit; } .loading { diff --git a/internal/ui/static/js/app.js b/internal/ui/static/js/app.js index cc3cbcbf..3f75ed40 100644 --- a/internal/ui/static/js/app.js +++ b/internal/ui/static/js/app.js @@ -135,7 +135,7 @@ function markPageAsRead() { updateEntriesStatus(entryIDs, "read", () => { // Make sure the Ajax request reach the server before we reload the page. - let element = document.querySelector("a[data-action=markPageAsRead]"); + let element = document.querySelector(":is(a, button)[data-action=markPageAsRead]"); let showOnlyUnread = false; if (element) { showOnlyUnread = element.dataset.showOnlyUnread || false; @@ -161,7 +161,7 @@ function handleEntryStatus(item, element, setToRead) { let toasting = !element; let currentEntry = findEntry(element); if (currentEntry) { - if (!setToRead || currentEntry.querySelector("a[data-toggle-status]").dataset.value == "unread") { + if (!setToRead || currentEntry.querySelector(":is(a, button)[data-toggle-status]").dataset.value == "unread") { toggleEntryStatus(currentEntry, toasting); } if (isListView() && currentEntry.classList.contains('current-item')) { @@ -180,7 +180,7 @@ function handleEntryStatus(item, element, setToRead) { // Change the entry status to the opposite value. function toggleEntryStatus(element, toasting) { let entryID = parseInt(element.dataset.id, 10); - let link = element.querySelector("a[data-toggle-status]"); + let link = element.querySelector(":is(a, button)[data-toggle-status]"); let currentStatus = link.dataset.value; let newStatus = currentStatus === "read" ? "unread" : "read"; @@ -259,7 +259,7 @@ function handleSaveEntry(element) { let toasting = !element; let currentEntry = findEntry(element); if (currentEntry) { - saveEntry(currentEntry.querySelector("a[data-save-entry]"), toasting); + saveEntry(currentEntry.querySelector(":is(a, button)[data-save-entry]"), toasting); } } @@ -299,7 +299,7 @@ function handleBookmark(element) { // Send the Ajax request and change the icon when bookmarking an entry. function toggleBookmark(parentElement, toasting) { - let element = parentElement.querySelector("a[data-toggle-bookmark]"); + let element = parentElement.querySelector(":is(a, button)[data-toggle-bookmark]"); if (!element) { return; } @@ -340,7 +340,7 @@ function handleFetchOriginalContent() { return; } - let element = document.querySelector("a[data-fetch-content-entry]"); + let element = document.querySelector(":is(a, button)[data-fetch-content-entry]"); if (!element) { return; } @@ -376,13 +376,13 @@ function openOriginalLink(openLinkInCurrentTab) { return; } - let currentItemOriginalLink = document.querySelector(".current-item a[data-original-link]"); + let currentItemOriginalLink = document.querySelector(".current-item :is(a, button)[data-original-link]"); if (currentItemOriginalLink !== null) { DomHelper.openNewTab(currentItemOriginalLink.getAttribute("href")); let currentItem = document.querySelector(".current-item"); // If we are not on the list of starred items, move to the next item - if (document.location.href != document.querySelector('a[data-page=starred]').href) { + if (document.location.href != document.querySelector(':is(a, button)[data-page=starred]').href) { goToListItem(1); } markEntryAsRead(currentItem); @@ -391,7 +391,7 @@ function openOriginalLink(openLinkInCurrentTab) { function openCommentLink(openLinkInCurrentTab) { if (!isListView()) { - let entryLink = document.querySelector("a[data-comments-link]"); + let entryLink = document.querySelector(":is(a, button)[data-comments-link]"); if (entryLink !== null) { if (openLinkInCurrentTab) { window.location.href = entryLink.getAttribute("href"); @@ -401,7 +401,7 @@ function openCommentLink(openLinkInCurrentTab) { return; } } else { - let currentItemCommentsLink = document.querySelector(".current-item a[data-comments-link]"); + let currentItemCommentsLink = document.querySelector(".current-item :is(a, button)[data-comments-link]"); if (currentItemCommentsLink !== null) { DomHelper.openNewTab(currentItemCommentsLink.getAttribute("href")); } @@ -437,7 +437,7 @@ function unsubscribeFromFeed() { * @param {boolean} fallbackSelf Refresh actual page if the page is not found. */ function goToPage(page, fallbackSelf) { - let element = document.querySelector("a[data-page=" + page + "]"); + let element = document.querySelector(":is(a, button)[data-page=" + page + "]"); if (element) { document.location.href = element.href; @@ -477,7 +477,7 @@ function goToFeed() { window.location.href = feedAnchor.href; } } else { - let currentItemFeed = document.querySelector(".current-item a[data-feed-link]"); + let currentItemFeed = document.querySelector(".current-item :is(a, button)[data-feed-link]"); if (currentItemFeed !== null) { window.location.href = currentItemFeed.getAttribute("href"); } @@ -574,7 +574,7 @@ function findEntry(element) { } function handleConfirmationMessage(linkElement, callback) { - if (linkElement.tagName != 'A') { + if (linkElement.tagName != 'A' && linkElement.tagName != "BUTTON") { linkElement = linkElement.parentNode; } @@ -592,8 +592,7 @@ function handleConfirmationMessage(linkElement, callback) { containerElement.appendChild(loadingElement); } - let yesElement = document.createElement("a"); - yesElement.href = "#"; + let yesElement = document.createElement("button"); yesElement.appendChild(document.createTextNode(linkElement.dataset.labelYes)); yesElement.onclick = (event) => { event.preventDefault(); @@ -603,8 +602,7 @@ function handleConfirmationMessage(linkElement, callback) { callback(linkElement.dataset.url, linkElement.dataset.redirectUrl); }; - let noElement = document.createElement("a"); - noElement.href = "#"; + let noElement = document.createElement("button"); noElement.appendChild(document.createTextNode(linkElement.dataset.labelNo)); noElement.onclick = (event) => { event.preventDefault(); @@ -677,7 +675,7 @@ function handlePlayerProgressionSave(playerElement) { * handle new share entires and already shared entries */ function handleShare() { - let link = document.querySelector('a[data-share-status]'); + let link = document.querySelector(':is(a, button)[data-share-status]'); let title = document.querySelector("body > main > section > header > h1 > a"); if (link.dataset.shareStatus === "shared") { checkShareAPI(title, link.href); diff --git a/internal/ui/static/js/bootstrap.js b/internal/ui/static/js/bootstrap.js index 9f8b9364..bfc89839 100644 --- a/internal/ui/static/js/bootstrap.js +++ b/internal/ui/static/js/bootstrap.js @@ -79,14 +79,14 @@ document.addEventListener("DOMContentLoaded", () => { } } - onClick("a[data-save-entry]", (event) => handleSaveEntry(event.target)); - onClick("a[data-toggle-bookmark]", (event) => handleBookmark(event.target)); - onClick("a[data-fetch-content-entry]", () => handleFetchOriginalContent()); - onClick("a[data-share-status]", () => handleShare()); - onClick("a[data-action=markPageAsRead]", (event) => handleConfirmationMessage(event.target, () => markPageAsRead())); - onClick("a[data-toggle-status]", (event) => handleEntryStatus("next", event.target)); + onClick(":is(a, button)[data-save-entry]", (event) => handleSaveEntry(event.target)); + onClick(":is(a, button)[data-toggle-bookmark]", (event) => handleBookmark(event.target)); + onClick(":is(a, button)[data-fetch-content-entry]", () => handleFetchOriginalContent()); + onClick(":is(a, button)[data-share-status]", () => handleShare()); + onClick(":is(a, button)[data-action=markPageAsRead]", (event) => handleConfirmationMessage(event.target, () => markPageAsRead())); + onClick(":is(a, button)[data-toggle-status]", (event) => handleEntryStatus("next", event.target)); - onClick("a[data-confirm]", (event) => handleConfirmationMessage(event.target, (url, redirectURL) => { + onClick(":is(a, button)[data-confirm]", (event) => handleConfirmationMessage(event.target, (url, redirectURL) => { let request = new RequestBuilder(url); request.withCallback((response) => {