docs: Enhance sidebar, remove jQuery dependency

Added the version selector to the sidebar directly rather than using JS injection by updating the configuration to include the improved version of the selector's html sidebar component.
This commit is contained in:
Richard Gazdik
2025-05-19 09:46:11 +02:00
committed by Gabor Kiss-Vamosi
parent 1906242741
commit 5b3d6d89f8
4 changed files with 23 additions and 158 deletions
-149
View File
@@ -1,149 +0,0 @@
{% extends "!page.html" %}
{# ------------------------------------------------------------------------
# Including jquery.js here is needed for the Furo theme. RTD theme included it automatically.
# Note: having 'sphinxcontrib.jquery' in `conf.py` extensions list isn't working for this reason:
# it is including jQuery via `<script src="_static/jquery.js?v=5d32c60e"></script>`
# at the VERY END of the HTML element, right before `</body>` and so the jQuery code in the
# page will not execute becuase the `$` jQuery object has not been created yet! This is
# despite including 'sphinxcontrib.jquery' in `conf.py` extensions list DOES include
# `jquery.js` at the top of the page in simpler projects! Until it is sorted out why
# this is happening, we're having to include jQuery.js manually in the below code,
# and make it possible by storing the same `jquery.js` file that Sphinx RTD theme used
# in the `./docs/src/_static/` directory.
#
# One big difference that might be relevant: sphinx_rtd_theme simple projects include
# `jquery.js` at the top of each HTML page. With Furo theme, it is including it at the end.
# This might be relevant.
#
# We have to use an absolute path here because this code will be atop every page and the relative
# path varies since there is currently a 5-deep directory structure among the HTML pages.
# A `<base>` URL could solve that. Getting it to use local (workstation) jsquery.js
# file could require some JavaScript emitting the correct `<base>` and `<script>` elements
# based on the beginning part of `document.location.href`.
# ------------------------------------------------------------------------
#}
{%- block extrahead -%}
{{ super() }}
<script type="text/javascript" src="https://docs.lvgl.io/master/_static/jquery.js"></script>
{% endblock %}
{% block footer %}
{# This "call" to super() here is needed because Furo theme has content in "block footer" which RTD
theme did not have. Without it, this "block footer" section wipes out Furo's parent versions. #}
{{ super() }}
<style>
.wy-side-nav-search > div[role="search"] {
color: black;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$(".toggle > *").hide();
$(".toggle .header").show();
$(".toggle .header").click(function() {
$(this).parent().children().not(".header").toggle(300);
$(this).parent().children(".header").toggleClass("open");
})
});
</script>
<script type="text/javascript">
{# -------------------------------------------------------------------------
# Called by 'DOMContentLoaded' event listener below; adds
# documentation-version dropdown list just above search form in
# upper part of left (nav) panel.
# ------------------------------------------------------------------------ #}
function add_version_selector()
{
return fetch("https://raw.githubusercontent.com/lvgl/docs_compiled/gh-pages/versionlist.txt")
.then(res => res.text())
.then(text => {
const version_list = text.split("\n").filter(version => version.trim().length > 0);
/* Note: class "sidebar-search-container" is part of Furo theme. */
let srch_form = document.getElementsByClassName("sidebar-search-container")[0];
let parent_div = srch_form.parentElement;
let select_elem_text = `
<select name="version" id="version_dropdown" onchange="ver_sel()">
${version_list.map(version => {
let versionName = "";
if(version == "master") versionName = "master (latest)";
else versionName = "v" + ((version.indexOf(".") != -1) ? version : (version + " (latest minor)"));
return `<option value="${version}">${versionName}</option>`;
})}
</select>`;
let node_array = jQuery.parseHTML(select_elem_text);
/* 1st node is a 'text' node. 2nd node is the <select> node.
* Insert <select> (dropdown list) node above search form. */
parent_div.insertBefore(node_array[1], srch_form);
});
}
{# -------------------------------------------------------------------------
# Fires when user selects a documentation version from version dropdown.
# ------------------------------------------------------------------------- #}
function ver_sel()
{
var x = document.getElementById("version_dropdown").value;
var new_url = window.location.protocol + "//" + window.location.host + "/" + x + "/";
if (new_url.startsWith('http')) {
window.location.href = new_url;
}
}
{# -------------------------------------------------------------------------
# Once: add documentation-version dropdown list just above search form in
# upper part of left panel.
# ------------------------------------------------------------------------- #}
document.addEventListener('DOMContentLoaded', (event) => {
add_version_selector().then(() => {
var value = window.location.pathname.split('/')[1];
document.getElementById("version_dropdown").value = value;
});
})
{# -------------------------------------------------------------------------
# This listener delays loading (slow-to-load) examples until they are
# actually in view in the browser's viewport.
#
# Once: for each "lv-example" class element, adds observer which watches
# for that element to come into view in the browser's viewport. When it
# does, `onIntersection()` function is fired, causing example to be loaded
# if it hasn't already, or removed when its intersectionRatio <= 0.
# ------------------------------------------------------------------------- #}
document.addEventListener('DOMContentLoaded', (event) => {
function onIntersection(entries) {
entries.forEach(entry => {
let currentlyLoaded = entry.target.getAttribute("data-is-loaded") == "true";
let shouldBeLoaded = entry.intersectionRatio > 0;
if(currentlyLoaded != shouldBeLoaded) {
entry.target.setAttribute("data-is-loaded", shouldBeLoaded);
if(shouldBeLoaded) {
let iframe = document.createElement("iframe");
iframe.src = entry.target.getAttribute("data-real-src");
entry.target.appendChild(iframe);
} else {
let iframe = entry.target.querySelector("iframe");
iframe.parentNode.removeChild(iframe);
}
}
});
}
const config = {
rootMargin: '600px 0px',
threshold: 0.01
};
let observer = new IntersectionObserver(onIntersection, config);
document.querySelectorAll(".lv-example").forEach(iframe => {
observer.observe(iframe);
});
});
</script>
{% endblock %}
+6
View File
@@ -0,0 +1,6 @@
<form class="sidebar-search-container" method="get" action="{{ pathto('search') }}" role="search">
<input class="sidebar-search" placeholder="{{ _("Search Documentation") }}" name="q" aria-label="{{ _("Search" ) }}">
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<div id="searchbox"></div>
@@ -0,0 +1,4 @@
<div class="version-selector">
<label for="version_dropdown">Select LVGL version</label>
<select name="version" id="version_dropdown"></select>
</div>
+13 -9
View File
@@ -224,18 +224,22 @@ html_theme_options = {
"source_repository": "https://github.com/lvgl/lvgl/",
"source_branch": "master",
"source_directory": "docs/src/",
"light_css_variables": {
"--font-stack": "\"Inter\", sans-serif",
"color-brand-primary": "#7C4DFF",
"color-brand-content": "#7C4DFF",
},
"dark_css_variables": {
"color-brand-primary": "#7C4DFF",
"color-brand-content": "#7C4DFF",
},
# "announcement": "<em>Semi-permanent announcement</em> from <code>conf.py</code>.",
}
html_sidebars = {
"**": [
"sidebar/brand.html",
"sidebar/version-selector.html",
"sidebar/search.html",
"sidebar/scroll-start.html",
"sidebar/navigation.html",
"sidebar/ethical-ads.html",
"sidebar/scroll-end.html",
"sidebar/variant-selector.html"
]
}
# For site map generation
if "LVGL_URLPATH" not in os.environ:
os.environ['LVGL_URLPATH'] = 'master'