Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pydata_sphinx_theme/docs-navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@
{% endif %}

{%- block icon_links -%}
{%- include "icon-links.html" with context -%}
{%- include "icon-links.html" with context -%}
{%- endblock %}

{% if theme_use_version_switch == true %}
{%- include "version-switcher.html" %}
{% endif %}
</li>
</ul>
</div>
</div>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pydata_sphinx_theme/static/webpack-macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

{% macro head_pre_bootstrap() %}
<link href="{{ pathto('_static/css/theme.css', 1) }}" rel="stylesheet" />
<link href="{{ pathto('_static/css/index.101715efdecc9b59cb6e1ddfa685c31f.css', 1) }}" rel="stylesheet" />
<link href="{{ pathto('_static/css/index.9cd52f7647f75bcab1282abf07de7a52.css', 1) }}" rel="stylesheet" />
{% endmacro %}

{% macro head_js_preload() %}
<link rel="preload" as="script" href="{{ pathto('_static/js/index.d8bbf5861d671d414e1a.js', 1) }}">
<link rel="preload" as="script" href="{{ pathto('_static/js/index.c9faddf98927557166f1.js', 1) }}">
{% endmacro %}

{% macro body_post() %}
<script src="{{ pathto('_static/js/index.d8bbf5861d671d414e1a.js', 1) }}"></script>
<script src="{{ pathto('_static/js/index.c9faddf98927557166f1.js', 1) }}"></script>
{% endmacro %}
5 changes: 5 additions & 0 deletions pydata_sphinx_theme/theme.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ search_bar_position = sidebar
navigation_with_keys = True
show_toc_level = 1
navbar_align = content
use_version_switch = True
version_switch_json_url = /versions.json
version_switch_enable_locale = True
version_switch_locales = zh, en

36 changes: 36 additions & 0 deletions pydata_sphinx_theme/version-switcher.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script type="text/javascript">
(function () {
window.versionSwitcher = {
pageName: "{{pagename}}.html",
versionJsonUrl: "{{theme_version_switch_json_url}}",
enableLocaleSupport: "{{theme_version_switch_enable_locale}}" === "True",
// TODO read from "{{theme_version_switch_locales}}"
allLocales: [
{
"locale": "zh",
"display": "中文"
},
{
"locale": "en",
"display": "EN"
}
]
}
})();
</script>

<ul class="navbar-nav">
<li class="nav-item dropdown">
<button id="version-dropdown" class="btn btn-secondary btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<!-- placeholder for javascript filling above -->
</button>
<div id="version-menu" class="dropdown-menu" style="min-width: 6rem;">
<!-- placeholder for javascript filling above -->
</div>
</li>
<li class="nav-item">
<span id="locale-switcher">
<!-- placeholder for locale switcher -->
</span>
</li>
</ul>
202 changes: 202 additions & 0 deletions src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,205 @@ function addTOCInteractivity() {
$(document).ready(() => {
addTOCInteractivity();
});


function setupVersionSwitcher() {
// Setup Version Switcher

// Only enable version switcher when window.versionSwitcher is filled by sphinx
if (!window.versionSwitcher) {
return;
}
let pageName = window.versionSwitcher.pageName;
let versionJsonUrl = window.versionSwitcher.versionJsonUrl;
let enableLocaleSupport = window.versionSwitcher.enableLocaleSupport;
let allLocales = window.versionSwitcher.allLocales;

// Remote version should like this.
// .name and .alias must be unique in each locale.
// It's not necessary to have same versions in all locales.
// 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.

/*
let allVersions = {
"en": [
{
"name": "v1.2.0",
"url": "v1.2.0",
"alias": ["latest"],
"default": true
},
{
"name": "v1.1.0",
"url": "v1.1.0",
"alias": []
},
],
"zh":[
{
"name": "v1.0.0",
"url": "v1.0.0",
"alias": []
"default": true
},
],
};
*/
function parseCurrentURL() {
// parseCurrentURL look up current pathname, generate all information about current version and locale.

let pathname = window.location.pathname;

// add "index.html" back when browser omit it.
if (pageName.endsWith("index.html") && pathname.endsWith("/")) {
pathname += "index.html";
}
if (pathname.slice(-pageName.length) !== pageName) {
// Sphinx generated pages should have exactly same suffix
throw 'page suffix do not match requirements';
}

// Get base URL by Removing '/' and pageName
let baseURL = pathname.slice(0, -(pageName.length + 1));
let parts = baseURL.split('/');

let currentVersion = '';
let currentLocale = '';

if (enableLocaleSupport) {
if (parts.length < 1) {
throw 'page base URL do not have any locale information';
}
currentLocale = parts.pop();
}
if (parts.length < 1) {
throw 'page base URL do not have any locale information';
}
currentVersion = parts.pop();
// This is base URL without any version or locate.
let globalBaseURL = parts.join('/')

return {pageName, baseURL, currentVersion, currentLocale, globalBaseURL};
}

// validate Check currentLocale and currentVersion is valid.
// Return canonicalVersion: indicate current version's real name
function validate(allVersions, info) {
let locale = "default"; // Use default as key when locale feature is disabled
if (enableLocaleSupport) {
locale = info.currentLocale;
}
let version_list = allVersions[locale];

if (version_list === undefined) {
throw `locale '${locale}'doesn't exist in remote version mapping`;
}

let canonicalVersion = function() { // Match currentVersion in version_list, try to find canonical version name by matching name and alias
for (const v of version_list) {
if (info.currentVersion === v.name) {
return v.name;
}
for (const alias_name of v.alias) {
if (info.currentVersion === alias_name) {
return v.name;
}
}
}
throw `version '${info.currentVersion}' doesn't exist in remove version maaping`
}()

return canonicalVersion;
}

// Set locale or version to null to indicate unchanged property.
function constructUrl(info, targetLocale, targetVersion) {
let segments = [info.globalBaseURL];

if (targetLocale == null) {
targetLocale = info.currentLocale;
}
if (targetVersion == null) {
targetVersion = info.currentVersion;
}
segments.push(targetVersion);
if (enableLocaleSupport) {
segments.push(targetLocale);
}
segments.push(info.pageName);
return segments.join('/') + window.location.hash;
}

function render(allVersions, info) {
function onSwitchVersion(evt) {
evt.preventDefault()
let selected = evt.currentTarget.getAttribute('key');

// process with alias problem, e.g. do not jump if target is just an alias of current one.
if (selected == info.canonicalVersion) {
// Current page is already the target version, ignore
return;
}

let new_url = constructUrl(info, null, selected);
window.location.assign(new_url);
}

function onSwitchLocale(evt) {
evt.preventDefault()
let selected = evt.currentTarget.getAttribute('key');

let new_url = constructUrl(info, selected, null);
window.location.assign(new_url);
}

// Fill the current version in the dropdown, always show real name instead of alias
document.getElementById("version-dropdown").innerText = info.canonicalVersion;

const menuHTML = (function() {
return allVersions[info.currentLocale].map((version) => {
let text = version.name;
if (version.alias.length > 0) {
text = `${version.name} (${version.alias.join(' ')})`
}

return `<button class="dropdown-item" key="${version.name}">${text}</button>`
})
})().join('')
// fill the version menu
document.getElementById("version-menu").innerHTML = menuHTML;

// bind the changes to this menu to trigger the switching function
$('#version-menu button').on('click', onSwitchVersion)

// Adding locale switcher
const localeHTML = (function() {
return allLocales.map((l) => {
if (l.locale === info.currentLocale) {
return `<a class="locale-btn locale-current" key="${l.locale}">${l.display}</a>`
} else {
return `<a class="locale-btn locale-option "key="${l.locale}">${l.display}</a>`
}
})
})().join('/')
document.getElementById("locale-switcher").innerHTML = localeHTML;

$('#locale-switcher .locale-option').on('click', onSwitchLocale)
}

// Trigger fetch as earlier as possible to speedup page loading.
let p = fetch(versionJsonUrl).then((resp) => {
return resp.json()
});

let info = parseCurrentURL();

p.then((allVersions) => {
let canonicalVersion = validate(allVersions, info);
info.canonicalVersion = canonicalVersion;

render(allVersions, info);
})
}

$(document).ready(setupVersionSwitcher);
10 changes: 10 additions & 0 deletions src/scss/_navbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@
.navbar-header a {
padding: 0 15px;
}

.locale-btn {
padding: 0 5px !important;
&.locale-current {
color: #AAA;
}
&.locale-option {
cursor: pointer;
}
}