Skip to content

Commit

Permalink
feature: add version switcher
Browse files Browse the repository at this point in the history
  • Loading branch information
cheekyshibe committed Mar 25, 2021
1 parent b5351c5 commit 8c18986
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 7 deletions.
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;
}
}

0 comments on commit 8c18986

Please sign in to comment.