Skip to content

Commit b100d22

Browse files
committed
feature: add version switcher
1 parent b5351c5 commit b100d22

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

pydata_sphinx_theme/docs-navbar.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,11 @@
4343
{%- block icon_links -%}
4444
{%- include "icon-links.html" with context -%}
4545
{%- endblock %}
46+
47+
<ul class="navbar-nav">
48+
<li class="version_switcher nav-item dropdown">
49+
{%- include "version-switcher.html" %}
50+
</li>
51+
</ul>
4652
</div>
4753
</div>

pydata_sphinx_theme/theme.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ search_bar_position = sidebar
2121
navigation_with_keys = True
2222
show_toc_level = 1
2323
navbar_align = content
24+
use_version_switch = True
25+
version_switch_json_url = /versions.json
26+
version_switch_enable_locale = True
27+
version_switch_locales = zh, en
28+
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
{% if theme_use_version_switch == true %}
2+
<script type="text/javascript">
3+
(function () {
4+
const version_json_url = "{{theme_version_switch_json_url}}";
5+
const enable_locale = "{{theme_version_switch_enable_locale}}" === 'True';
6+
7+
// TODO check how to pass an array instead of raw string
8+
const all_locales = "{{theme_version_switch_locales}}";
9+
10+
// Remote version should like this.
11+
// .name and .alias must be unique in each locale.
12+
// It's not necessary to have same versions in all locales.
13+
// When locale is enabled, there must be only one .default=true item in each locale to indicate which one should be redirect if target version doesn't exist in target locale.
14+
15+
/*
16+
let all_versions = {
17+
"en": [
18+
{
19+
"name": "V1.2.0",
20+
"url": "v1.2.0",
21+
"alias": ["latest"],
22+
"default": true
23+
},
24+
{
25+
"name": "v1.1.0",
26+
"url": "v1.1.0",
27+
"alias": []
28+
},
29+
],
30+
"zh":[
31+
{
32+
"name": "v1.0.0",
33+
"url": "v1.0.0",
34+
"alias": []
35+
"default": true
36+
},
37+
],
38+
};
39+
*/
40+
41+
function parse_current_url() {
42+
// sphinx will fill here.
43+
let page_name = "{{pagename}}.html";
44+
45+
if (location.pathname.slice(-page_name.length) !== page_name) {
46+
// Sphinx generated pages should have exactly same suffix
47+
throw 'page suffix do not match requirements';
48+
}
49+
// Get base URL by Removing '/' and page_name
50+
let base_url = location.pathname.slice(0, -(page_name.length + 1));
51+
let parts = base_url.split('/');
52+
53+
let current_version = parts.pop();
54+
let current_locale = '';
55+
56+
if (enable_locale) {
57+
if (parts.length < 1) {
58+
throw 'page base URL do not have any locale information';
59+
}
60+
current_locale = parts.pop();
61+
}
62+
// This is base URL without any version or locate.
63+
let global_base_url = parts.join('/')
64+
65+
return {page_name, base_url, current_version, current_locale, global_base_url};
66+
}
67+
68+
function validate(all_versions, info) {
69+
// TODO check all_versions is valid
70+
71+
// TODO check current_locale and current_version is valid
72+
return;
73+
}
74+
75+
// Set locale or version to null to indicate unchanged property.
76+
function construct_url(info, target_locale, target_version) {
77+
let segments = [info.global_base_url];
78+
79+
if (target_locale == null) {
80+
target_locale = info.current_locale;
81+
}
82+
if (target_version == null) {
83+
target_version = info.current_version;
84+
}
85+
if (enable_locale) {
86+
segments.push(target_locale);
87+
}
88+
segments.push(target_version);
89+
segments.push(info.page_name);
90+
return segments.join('/') + location.hash;
91+
}
92+
93+
function render(all_versions, info) {
94+
function on_switch(evt) {
95+
evt.preventDefault()
96+
let selected = evt.currentTarget.getAttribute('key');
97+
98+
// TODO process with alias problem, e.g. do not jump if target is just an alias of current one.
99+
let new_url = construct_url(info, null, selected);
100+
window.location.assign(new_url);
101+
}
102+
// fill the current version in the dropdown
103+
document.getElementById("version-dropdown").innerText = 'latest';
104+
105+
// TODO show alias as well
106+
const getVersionLink = () => {
107+
return all_versions[info.current_locale].map((item) => {
108+
return item.name;
109+
}).map(key => `<button class="dropdown-item" key="${key}">${key}<\/button>`)
110+
}
111+
// fill the version menu
112+
document.getElementById("version-menu").innerHTML = getVersionLink().join('');
113+
114+
// bind the changes to this menu to trigger the switching function
115+
$('#version-menu button').on('click', on_switch)
116+
117+
// TODO add locale button
118+
}
119+
120+
121+
// Trigger fetch as earlier as possible to speedup page loading.
122+
let p = fetch(version_json_url).then((resp) => {
123+
return resp.json()
124+
});
125+
126+
$(document).ready(function () {
127+
let info = parse_current_url();
128+
129+
p.then((all_versions) => {
130+
validate(all_versions, info);
131+
render(all_versions, info);
132+
})
133+
});
134+
})();
135+
136+
</script>
137+
138+
<button id="version-dropdown" class="btn btn-secondary btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
139+
<!-- placeholder for javascript filling above -->
140+
</button>
141+
<div id="version-menu" class="dropdown-menu" style="min-width: 6rem;">
142+
<!-- placeholder for javascript filling above -->
143+
</div>
144+
145+
{% endif %}

0 commit comments

Comments
 (0)