8000 Update ``python-docs-theme`` to work with Sphinx 5 & 6 (#99) · python/python-docs-theme@32115c4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 32115c4

Browse files
authored
Update python-docs-theme to work with Sphinx 5 & 6 (#99)
* Use double quotation marks in ``__init__.py`` * Drop semicolons in ``menu.js`` * Use braces for if-statements in ``menu.js`` * Use double quotation marks in ``menu.js`` * Mark a parameter as unused in ``menu.js`` * Drop jQuery in ``sidebars.js`` * Drop jQuery in ``copybutton.js`` * Introduce ``getHideableCopyButtonElements()`` * Mild JavaScript refactor
1 parent 4609599 commit 32115c4

File tree

6 files changed

+230
-210
lines changed

6 files changed

+230
-210
lines changed

python_docs_theme/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,11 @@ def _html_page_context(
5252

5353
def setup(app):
5454
current_dir = os.path.abspath(os.path.dirname(__file__))
55-
app.add_html_theme(
56-
'python_docs_theme', current_dir)
55+
app.add_html_theme("python_docs_theme", current_dir)
5756

5857
app.connect("html-page-context", _html_page_context)
5958

6059
return {
61-
'parallel_read_safe': True,
62-
'parallel_write_safe': True,
60+
"parallel_read_safe": True,
61+
"parallel_write_safe": True,
6362
}
Lines changed: 84 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,92 @@
1-
$(document).ready(function() {
2-
/* Add a [>>>] button on the top-right corner of code samples to hide
1+
// ``function*`` denotes a generator in JavaScript, see
2+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
3+
function* getHideableCopyButtonElements(rootElement) {
4+
// yield all elements with the "go" (Generic.Output),
5+
// "gp" (Generic.Prompt), or "gt" (Generic.Traceback) CSS class
6+
for (const el of rootElement.querySelectorAll('.go, .gp, .gt')) {
7+
yield el
8+
}
9+
// tracebacks (.gt) contain bare text elements that need to be
10+
// wrapped in a span to hide or show the element
11+
for (let el of rootElement.querySelectorAll('.gt')) {
12+
while ((el = el.nextSibling) && el.nodeType !== Node.DOCUMENT_NODE) {
13+
// stop wrapping text nodes when we hit the next output or
14+
// prompt element
15+
if (el.nodeType === Node.ELEMENT_NODE && el.matches(".gp, .go")) {
16+
break
17+
}
18+
// if the node is a text node with content, wrap it in a
19+
// span element so that we can control visibility
20+
if (el.nodeType === Node.TEXT_NODE && el.textContent.trim()) {
21+
const wrapper = document.createElement("span")
22+
el.after(wrapper)
23+
wrapper.appendChild(el)
24+
el = wrapper
25+
}
26+
yield el
27+
}
28+
}
29+
}
30+
31+
32+
const loadCopyButton = () => {
33+
/* Add a [>>>] button in the top-right corner of code samples to hide
334
* the >>> and ... prompts and the output and thus make the code
435
* copyable. */
5-
var div = $('.highlight-python .highlight,' +
6-
'.highlight-python3 .highlight,' +
7-
'.highlight-pycon .highlight,' +
8-
'.highlight-pycon3 .highlight,' +
9-
'.highlight-default .highlight');
10-
var pre = div.find('pre');
36+
const hide_text = "Hide the prompts and output"
37+
const show_text = "Show the prompts and output"
1138

12-
// get the styles from the current theme
13-
pre.parent().parent().css('position', 'relative');
14-
var hide_text = 'Hide the prompts and output';
15-
var show_text = 'Show the prompts and output';
16-
var border_width = pre.css('border-top-width');
17-
var border_style = pre.css('border-top-style');
18-
var border_color = pre.css('border-top-color');
19-
var button_styles = {
20-
'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0',
21-
'border-color': border_color, 'border-style': border_style,
22-
'border-width': border_width, 'color': border_color, 'text-size': '75%',
23-
'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em',
24-
'border-radius': '0 3px 0 0'
39+
const button = document.createElement("span")
40+
button.classList.add("copybutton")
41+
button.innerText = ">>>"
42+
button.title = hide_text
43+
button.dataset.hidden = "false"
44+
const buttonClick = event => {
45+
// define the behavior of the button when it's clicked
46+
event.preventDefault()
47+
const buttonEl = event.currentTarget
48+
const codeEl = buttonEl.nextElementSibling
49+
if (buttonEl.dataset.hidden === "false") {
50+
// hide the code output
51+
for (const el of getHideableCopyButtonElements(codeEl)) {
52+
el.hidden = true
53+
}
54+
buttonEl.title = show_text
55+
buttonEl.dataset.hidden = "true"
56+
} else {
57+
// show the code output
58+
for (const el of getHideableCopyButtonElements(codeEl)) {
59+
el.hidden = false
60+
}
61+
buttonEl.title = hide_text
62+
buttonEl.dataset.hidden = "false"
63+
}
2564
}
2665

66+
const highlightedElements = document.querySelectorAll(
67+
".highlight-python .highlight,"
68+
+ ".highlight-python3 .highlight,"
69+
+ ".highlight-pycon .highlight,"
70+
+ ".highlight-pycon3 .highlight,"
71+
+ ".highlight-default .highlight"
72+
)
73+
2774
// create and add the button to all the code blocks that contain >>>
28-
div.each(function(index) {
29-
var jthis = $(this);
30-
if (jthis.find('.gp').length > 0) {
31-
var button = $('<span class="copybutton">&gt;&gt;&gt;</span>');
32-
button.css(button_styles)
33-
button.attr('title', hide_text);
34-
button.data('hidden', 'false');
35-
jthis.prepend(button);
36-
}
37-
// tracebacks (.gt) contain bare text elements that need to be
38-
// wrapped in a span to work with .nextUntil() (see later)
39-
jthis.find('pre:has(.gt)').contents().filter(function() {
40-
return ((this.nodeType == 3) && (this.data.trim().length > 0));
41-
}).wrap('<span>');
42-
});
75+
highlightedElements.forEach(el => {
76+
el.style.position = "relative"
4377

44-
// define the behavior of the button when it's clicked
45-
$('.copybutton').click(function(e){
46-
e.preventDefault();
47-
var button = $(this);
48-
if (button.data('hidden') === 'false') {
49-
// hide the code output
50-
button.parent().find('.go, .gp, .gt').hide();
51-
button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');
52-
button.css('text-decoration', 'line-through');
53-
button.attr('title', show_text);
54-
button.data('hidden', 'true');
55-
} else {
56-
// show the code output
57-
button.parent().find('.go, .gp, .gt').show();
58-
button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');
59-
button.css('text-decoration', 'none');
60-
button.attr('title', hide_text);
61-
button.data('hidden', 'false');
78+
// if we find a console prompt (.gp), prepend the (deeply cloned) button
79+
const clonedButton = button.cloneNode(true)
80+
// the onclick attribute is not cloned, set it on the new element
81+
clonedButton.onclick = buttonClick
82+
if (el.querySelector(".gp") !== null) {
83+
el.prepend(clonedButton)
6284
}
63-
});
64-
});
85+
})
86+
}
87+
88+
if (document.readyState !== "loading") {
89+
loadCopyButton()
90+
} else {
91+
document.addEventListener("DOMContentLoaded", loadCopyButton)
92+
}

python_docs_theme/static/menu.js

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,57 @@
1-
document.addEventListener('DOMContentLoaded', function () {
1+
document.addEventListener("DOMContentLoaded", function () {
22

33
// Make tables responsive by wrapping them in a div and making them scrollable
4-
const tables = document.querySelectorAll('table.docutils');
4+
const tables = document.querySelectorAll("table.docutils")
55
tables.forEach(function(table){
6-
table.outerHTML = '<div class="responsive-table__container">' + table.outerHTML + '</div>'
7-
});
6+
table.outerHTML = '<div class="responsive-table__container">' + table.outerHTML + "</div>"
7+
})
88

9-
const togglerInput = document.querySelector('.toggler__input');
10-
const togglerLabel = document.querySelector('.toggler__label');
11-
const sideMenu = document.querySelector('.menu-wrapper');
12-
const menuItems = document.querySelectorAll('.menu')
13-
const doc = document.querySelector('.document');
14-
const body = document.querySelector('body');
9+
const togglerInput = document.querySelector(".toggler__input")
10+
const togglerLabel = document.querySelector(".toggler__label")
11+
const sideMenu = document.querySelector(".menu-wrapper")
12+
const menuItems = document.querySelectorAll(".menu")
13+
const doc = document.querySelector(".document")
14+
const body = document.querySelector("body")
1515

1616
function closeMenu() {
17-
togglerInput.checked = false;
18-
sideMenu.setAttribute("aria-expanded", 'false');
19-
sideMenu.setAttribute('aria-hidden', 'true');
20-
togglerLabel.setAttribute('aria-pressed', 'false');
21-
body.style.overflow = 'visible';
17+
togglerInput.checked = false
18+
sideMenu.setAttribute("aria-expanded", "false")
19+
sideMenu.setAttribute("aria-hidden", "true")
20+
togglerLabel.setAttribute("aria-pressed", "false")
21+
body.style.overflow = "visible"
2222
}
2323
function openMenu() {
24-
togglerInput.checked = true;
25-
sideMenu.setAttribute("aria-expanded", 'true');
26-
sideMenu.setAttribute('aria-hidden', 'false');
27-
togglerLabel.setAttribute('aria-pressed', 'true');
28-
body.style.overflow = 'hidden';
24+
togglerInput.checked = true
25+
sideMenu.setAttribute("aria-expanded", "true")
26+
sideMenu.setAttribute("aria-hidden", "false")
27+
togglerLabel.setAttribute("aria-pressed", "true")
28+
body.style.overflow = "hidden"
2929
}
3030

3131
// Close menu when link on the sideMenu is clicked
32-
sideMenu.addEventListener('click', function (event) {
33-
let target = event.target;
34-
if (target.tagName.toLowerCase() !== 'a') return;
35-
closeMenu();
32+
sideMenu.addEventListener("click", function (event) {
33+
let target = event.target
34+
if (target.tagName.toLowerCase() !== "a") {
35+
return
36+
}
37+
closeMenu()
3638
})
3739
// Add accessibility data when sideMenu is opened/closed
38-
togglerInput.addEventListener('change', function (e) {
39-
togglerInput.checked ? openMenu() : closeMenu();
40-
});
40+
togglerInput.addEventListener("change", function (_event) {
41+
togglerInput.checked ? openMenu() : closeMenu()
42+
})
4143
// Make sideMenu links tabbable only when visible
4244
for(let menuItem of menuItems) {
4345
if(togglerInput.checked) {
44-
menuItem.setAttribute('tabindex', '0');
46+
menuItem.setAttribute("tabindex", "0")
4547
} else {
46-
menuItem.setAttribute('tabindex', '-1');
48+
menuItem.setAttribute("tabindex", "-1")
4749
}
4850
}
4951
// Close sideMenu when document body is clicked
50-
doc.addEventListener('click', function () {
52+
doc.addEventListener("click", function () {
5153
if (togglerInput.checked) {
52-
closeMenu();
54+
closeMenu()
5355
}
5456
})
5557
})

python_docs_theme/static/pydoctheme.css

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ div.sphinxsidebar h4 {
104104
}
105105

106106
div.sphinxsidebarwrapper {
107+
width: 217px;
107108
box-sizing: border-box;
108109
height: 100%;
109110
overflow-x: hidden;
110111
overflow-y: auto;
112+
float: left;
111113
}
112114

113115
div.sphinxsidebarwrapper > h3:first-child {
@@ -135,6 +137,33 @@ div.sphinxsidebar input[type='text'] {
135137
max-width: 150px;
136138
}
137139

140+
#sidebarbutton {
141+
/* Sphinx 4.x and earlier compat */
142+
height: 100%;
143+
background-color: #CCCCCC;
144+
margin-left: 0;
145+
color: #444444;
146+
font-size: 1.2em;
147+
cursor: pointer;
148+
padding-top: 1px;
149+
float: right;
150+
display: table;
151+
/* after Sphinx 4.x and earlier is dropped, only the below is needed */
152+
width: 12px;
153+
border-radius: 0 5px 5px 0;
154+
border-left: none;
155+
}
156+
157+
#sidebarbutton span {
158+
/* Sphinx 4.x and earlier compat */
159+
display: table-cell;
160+
vertical-align: middle;
161+
}
162+
163+
#sidebarbutton:hover {
164+
background-color: #AAAAAA;
165+
}
166+
138167
div.body {
139168
padding: 0 0 0 1.2em;
140169
}
@@ -279,6 +308,26 @@ div.genindex-jumpbox a {
279308
text-align: center;
280309
}
281310

311+
.copybutton {
312+
cursor: pointer;
313+
position: absolute;
314+
top: 0;
315+
right: 0;
316+
text-size: 75%;
317+
font-family: monospace;
318+
padding-left: 0.2em;
319+
padding-right: 0.2em;
320+
border-radius: 0 3px 0 0;
321+
color: #ac9; /* follows div.body pre */
322+
border-color: #ac9; /* follows div.body pre */
323+
border-style: solid; /* follows div.body pre */
324+
border-width: 1px; /* follows div.body pre */
325+
}
326+
327+
.copybutton[data-hidden='true'] {
328+
text-decoration: line-through;
329+
}
330+
282331
@media (max-width: 1023px) {
283332
/* Body layout */
284333
div.body {

0 commit comments

Comments
 (0)
0