From 9769c1aeda95b7c8733bcf72ea538a7363f67e8b Mon Sep 17 00:00:00 2001 From: Hofi Date: Fri, 10 May 2024 21:20:37 +0200 Subject: [PATCH] Tooltips now should look and behave the same for page titles and other links Signed-off-by: Hofi --- _js/custom/navigation.js | 105 +++++++++++++++++++++++++++------------ _js/main.min.js | 61 +++++++++++++++-------- 2 files changed, 114 insertions(+), 52 deletions(-) diff --git a/_js/custom/navigation.js b/_js/custom/navigation.js index cbaba3af..2dc63f19 100644 --- a/_js/custom/navigation.js +++ b/_js/custom/navigation.js @@ -24,12 +24,12 @@ $(function () { var tocMenuElement = tocElement.querySelector('.toc__menu'); if (null == tocMenuElement || false == tocMenuElement.hasChildNodes) shouldHide = true; - } - if (shouldHide) { - // TOC is autogenerated via 'include toc.html' so its size is not known prior the call of toc.html - // Signal emptiness, css will hide if needed based on it and config settings - // NOTE: Not hiding directly here to behave the same way like the left sidebar does - tocElement.classList.add('empty'); + if (shouldHide) { + // TOC is autogenerated via 'include toc.html' so its size is not known prior the call of toc.html + // Signal emptiness, css will hide if needed based on it and config settings + // NOTE: Not hiding directly here to behave the same way like the left sidebar does + tocElement.classList.add('empty'); + } } } @@ -298,7 +298,7 @@ $(function () { // Add anchors for headings // ------------- function addPageAnchors() { - // FIXME: This magic 6 must be maintained together now with generate_links.rb (and other places ?!) + // FIXME: This magic 6 must be maintained together now with generate_links.rb (and other places ?!), eliminate it! $('.page__content').find('h1, h2, h3, h4, h5, h6').each(function () { var id = $(this).attr('id'); if (id) { @@ -312,21 +312,37 @@ $(function () { }); } - function alterPageForTooltip(content, fullPageContent) { + // To support page links even better if possible in case the page has no description / subtitle, but has some text + // right after the title and before the first heading is presented, it will be treated as the description and will be handled here. + // This part should also handle the case when only the title presented. (a.k.a. not showing the tooltip in that case), + // also responsible to create a uniform look and fell both for page title tooltips and other link tooltips. + // + function alterContentForTooltip(content, url, isFullPageContent) { let tempContainer = document.createElement('div'); tempContainer.innerHTML = content; - if (fullPageContent) - hideTocIfNotNeeded(tempContainer, true); + hideTocIfNotNeeded(tempContainer, true); // Remove/Override some default title style formatting to look better in the tooltip - const pageTitle = tempContainer.querySelector('#page-title'); - if (pageTitle) - pageTitle.style.marginTop = '1em'; - - const pageSubtitle = tempContainer.querySelector('#page-subtitle'); - if (pageSubtitle) - pageSubtitle.style.borderBottom = '0px'; + var pageTitle = tempContainer.querySelector('#page-title'); + if (pageTitle == null) { + // If there is no page title, replace the first heading with an item looks and behaves like the page title + // to have similar result both for page title tooltips and other link item tooltips + // FIXME: This magic 6 must be maintained together now with generate_links.rb (and other places ?!), eliminate it! + var firstHeading = tempContainer.querySelector("h2, h3, h4, h5, h6"); + if (firstHeading) { + // Everything bellow must exist, so intentionally there's no error handling, let it rise + pageTitle = document.querySelector('#page-title').cloneNode(true); + pageTitle.id = firstHeading.id; + + const anchorElement = pageTitle.querySelector("a"); + anchorElement.textContent = firstHeading.textContent; + anchorElement.href = url; + + tempContainer.replaceChild(pageTitle, firstHeading); + } + } + pageTitle.style.marginTop = '1em'; var newContent = tempContainer.innerHTML // remove unnecessary, reqursive inner content tooltips @@ -350,22 +366,37 @@ $(function () { newContent => { var startHeading = newContent.querySelector('#' + startHeadingId); if (startHeading) { - var content = startHeading.outerHTML; // Include the starting element itself + var content = ''; + var heading = startHeading.outerHTML; // Include the starting element itself var nextSibling = startHeading.nextElementSibling; - // Collect all siblings until the next heading or the end of the document - // FIXME: This magic 6 must be maintained together now with generate_links.rb (and other places ?!) + // If handling a page title it will not have next sibling normally at all (we are removed and handling differently the description from the generated page) + // In that case the description is all the normal texts parts in the content, from the top (right bellow the title) to the first heading , try to get, it if there's any. + // If not presented, drop the whole content, return an empty one (a.k.a. do not show title only tooltips) + if (nextSibling == null && false == hasAnchor) { + startHeading = newContent.querySelector('.page__content'); + nextSibling = startHeading.firstElementChild; + // First element is the TOC, skip it to be able to produce an empty content + if (nextSibling && nextSibling.classList.contains('sidebar__right')) + nextSibling = nextSibling.nextElementSibling; + } + // Collect all siblings until the next heading or the end of the initial content + // FIXME: This magic 6 must be maintained together now with generate_links.rb (and other places ?!), eliminate it! while (nextSibling && nextSibling.tagName !== 'H1' && nextSibling.tagName !== 'H2' && nextSibling.tagName !== 'H3' && nextSibling.tagName !== 'H4' && nextSibling.tagName !== 'H5' && nextSibling.tagName !== 'H6') { content += nextSibling.outerHTML; nextSibling = nextSibling.nextElementSibling; } + + if (content.length != 0 || hasAnchor) + content = heading + content; + onSuccess(content); } else - console.error('Start heading not found by ID: ' + startHeadingId); + onError('Start heading not found by ID: ' + startHeadingId); }, error => { - error(error); + onError(error); } ); } @@ -402,10 +433,10 @@ $(function () { tooltip.style.setProperty(posName, newPosition); } - function showTooltip(event, tooltipText, fullPageContent) { + function showTooltip(event, tooltipText, isFullPageContent) { tooltip.innerHTML = tooltipText.innerHTML; - if (fullPageContent) + if (isFullPageContent) tooltip.classList.add("full-content-tooltip"); else tooltip.classList.remove("full-content-tooltip"); @@ -470,7 +501,7 @@ $(function () { element.appendChild(tooltipText); element.addEventListener('mouseover', function (event) { - var fullPageContent = element.classList.contains('full-content-tooltip'); + var isFullPageContent = element.classList.contains('full-content-tooltip'); tooltipTarget = element; @@ -481,18 +512,28 @@ $(function () { function onSuccess(newContent) { if (typeof (newContent) === 'object' && 'innerHTML' in newContent) newContent = newContent.innerHTML; - newContent = alterPageForTooltip(newContent, fullPageContent); - - // cache for reuse - tooltipText.innerHTML = newContent; - showTooltip(event, tooltipText, fullPageContent); + newContent = alterContentForTooltip(newContent, url. isFullPageContent); + + if (newContent.length > 0) { + // cache for reuse + tooltipText.innerHTML = newContent; + showTooltip(event, tooltipText, isFullPageContent); + } + else { + // Quick navigation from another link with tooltip to this link would keep alive the previous tooltip + // force close it, as we don't have tooltip for the current and this is the live hovered one. + hideTooltip(false); + } } function onError(error) { + // Quick navigation from another link with tooltip to this failing link would keep alive the previous tooltip + // force close it, as we don't have tooltip for the current and this is the live hovered one. + hideTooltip(false); console.error('Error loading the tooltip content!' + error); } - if (fullPageContent) { + if (isFullPageContent) { loadContentFromUrl(url, newContent => onSuccess(newContent), error => onError(error)); } else { @@ -500,7 +541,7 @@ $(function () { } } else - showTooltip(event, tooltipText, fullPageContent); + showTooltip(event, tooltipText, isFullPageContent); }); }); diff --git a/_js/main.min.js b/_js/main.min.js index 74212761..d6e5161f 100644 --- a/_js/main.min.js +++ b/_js/main.min.js @@ -8466,9 +8466,9 @@ $(function() { if (tocElement) { var tocMenuElement = tocElement.querySelector(".toc__menu"); if (null == tocMenuElement || false == tocMenuElement.hasChildNodes) shouldHide = true; - } - if (shouldHide) { - tocElement.classList.add("empty"); + if (shouldHide) { + tocElement.classList.add("empty"); + } } } function adjustSidebars() { @@ -8635,14 +8635,23 @@ $(function() { } }); } - function alterPageForTooltip(content, fullPageContent) { + function alterContentForTooltip(content, url, isFullPageContent) { let tempContainer = document.createElement("div"); tempContainer.innerHTML = content; - if (fullPageContent) hideTocIfNotNeeded(tempContainer, true); - const pageTitle = tempContainer.querySelector("#page-title"); - if (pageTitle) pageTitle.style.marginTop = "1em"; - const pageSubtitle = tempContainer.querySelector("#page-subtitle"); - if (pageSubtitle) pageSubtitle.style.borderBottom = "0px"; + hideTocIfNotNeeded(tempContainer, true); + var pageTitle = tempContainer.querySelector("#page-title"); + if (pageTitle == null) { + var firstHeading = tempContainer.querySelector("h2, h3, h4, h5, h6"); + if (firstHeading) { + pageTitle = document.querySelector("#page-title").cloneNode(true); + pageTitle.id = firstHeading.id; + const anchorElement = pageTitle.querySelector("a"); + anchorElement.textContent = firstHeading.textContent; + anchorElement.href = url; + tempContainer.replaceChild(pageTitle, firstHeading); + } + } + pageTitle.style.marginTop = "1em"; var newContent = tempContainer.innerHTML; newContent = newContent.replace(/\bcontent-tooltip\b/g, ""); return newContent; @@ -8655,16 +8664,23 @@ $(function() { loadContentFromUrl(url, newContent => { var startHeading = newContent.querySelector("#" + startHeadingId); if (startHeading) { - var content = startHeading.outerHTML; + var content = ""; + var heading = startHeading.outerHTML; var nextSibling = startHeading.nextElementSibling; + if (nextSibling == null && false == hasAnchor) { + startHeading = newContent.querySelector(".page__content"); + nextSibling = startHeading.firstElementChild; + if (nextSibling && nextSibling.classList.contains("sidebar__right")) nextSibling = nextSibling.nextElementSibling; + } while (nextSibling && nextSibling.tagName !== "H1" && nextSibling.tagName !== "H2" && nextSibling.tagName !== "H3" && nextSibling.tagName !== "H4" && nextSibling.tagName !== "H5" && nextSibling.tagName !== "H6") { content += nextSibling.outerHTML; nextSibling = nextSibling.nextElementSibling; } + if (content.length != 0 || hasAnchor) content = heading + content; onSuccess(content); - } else console.error("Start heading not found by ID: " + startHeadingId); + } else onError("Start heading not found by ID: " + startHeadingId); }, error => { - error(error); + onError(error); }); } const toolTipArrowSize = 10; @@ -8689,9 +8705,9 @@ $(function() { var newPosition = position + "px"; tooltip.style.setProperty(posName, newPosition); } - function showTooltip(event, tooltipText, fullPageContent) { + function showTooltip(event, tooltipText, isFullPageContent) { tooltip.innerHTML = tooltipText.innerHTML; - if (fullPageContent) tooltip.classList.add("full-content-tooltip"); else tooltip.classList.remove("full-content-tooltip"); + if (isFullPageContent) tooltip.classList.add("full-content-tooltip"); else tooltip.classList.remove("full-content-tooltip"); var tooltipPos = getTooltipPos(event, tooltipTarget); var tooltipArrowLeftShift = 2 * toolTipArrowSize; setArrowPosition("--tooltip-arrow-top", -1 * toolTipArrowSize); @@ -8733,25 +8749,30 @@ $(function() { tooltipText.textContent = ""; element.appendChild(tooltipText); element.addEventListener("mouseover", function(event) { - var fullPageContent = element.classList.contains("full-content-tooltip"); + var isFullPageContent = element.classList.contains("full-content-tooltip"); tooltipTarget = element; if (tooltipText.innerHTML === "") { var url = element.href; function onSuccess(newContent) { if (typeof newContent === "object" && "innerHTML" in newContent) newContent = newContent.innerHTML; - newContent = alterPageForTooltip(newContent, fullPageContent); - tooltipText.innerHTML = newContent; - showTooltip(event, tooltipText, fullPageContent); + newContent = alterContentForTooltip(newContent, url.isFullPageContent); + if (newContent.length > 0) { + tooltipText.innerHTML = newContent; + showTooltip(event, tooltipText, isFullPageContent); + } else { + hideTooltip(false); + } } function onError(error) { + hideTooltip(false); console.error("Error loading the tooltip content!" + error); } - if (fullPageContent) { + if (isFullPageContent) { loadContentFromUrl(url, newContent => onSuccess(newContent), error => onError(error)); } else { loadContentPartFrom(url, newContent => onSuccess(newContent), error => onError(error)); } - } else showTooltip(event, tooltipText, fullPageContent); + } else showTooltip(event, tooltipText, isFullPageContent); }); }); document.addEventListener("mousemove", function(event) {