auspice
Version:
Web app for visualizing pathogen evolution
235 lines (213 loc) • 7.28 kB
JavaScript
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const classNames = require('classnames');
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
const translation = require('../../server/translation.js');
const {getPath, idx} = require('../utils.js');
class SideNav extends React.Component {
// return appropriately translated category string
getLocalizedCategoryString(category) {
const categoryString =
idx(translation, [
this.props.language,
'localized-strings',
'categories',
category,
]) || category;
return categoryString;
}
// return appropriately translated label to use for doc/blog in sidebar
getLocalizedString(metadata) {
let localizedString;
const i18n = translation[this.props.language];
const id = metadata.localized_id;
const sbTitle = metadata.sidebar_label;
if (sbTitle) {
localizedString =
idx(i18n, ['localized-strings', 'docs', id, 'sidebar_label']) ||
sbTitle;
} else {
localizedString =
idx(i18n, ['localized-strings', 'docs', id, 'title']) || metadata.title;
}
return localizedString;
}
// return link to doc in sidebar
getLink(metadata) {
if (metadata.permalink) {
const targetLink = getPath(metadata.permalink, siteConfig.cleanUrl);
if (targetLink.match(/^https?:/)) {
return targetLink;
}
return siteConfig.baseUrl + targetLink;
}
if (metadata.path) {
return `${siteConfig.baseUrl}blog/${getPath(
metadata.path,
siteConfig.cleanUrl,
)}`;
}
return null;
}
renderCategory = categoryItem => {
let ulClassName = '';
let categoryClassName = 'navGroupCategoryTitle';
let arrow;
if (this.props.collapsible) {
categoryClassName += ' collapsible';
ulClassName = 'hide';
arrow = (
<span className="arrow">
<svg width="24" height="24" viewBox="0 0 24 24">
<path
fill="#565656"
d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"
/>
<path d="M0 0h24v24H0z" fill="none" />
</svg>
</span>
);
}
return (
<div className="navGroup" key={categoryItem.title}>
<h3 className={categoryClassName}>
{this.getLocalizedCategoryString(categoryItem.title)}
{arrow}
</h3>
<ul className={ulClassName}>
{categoryItem.children.map(item => {
switch (item.type) {
case 'LINK':
return this.renderItemLink(item);
case 'SUBCATEGORY':
return this.renderSubcategory(item);
default:
return null;
}
})}
</ul>
</div>
);
};
renderSubcategory = subcategoryItem => (
<div className="navGroup subNavGroup" key={subcategoryItem.title}>
<h4 className="navGroupSubcategoryTitle">
{this.getLocalizedCategoryString(subcategoryItem.title)}
</h4>
<ul>{subcategoryItem.children.map(this.renderItemLink)}</ul>
</div>
);
renderItemLink = linkItem => {
const linkMetadata = linkItem.item;
const itemClasses = classNames('navListItem', {
navListItemActive: linkMetadata.id === this.props.current.id,
});
return (
<li className={itemClasses} key={linkMetadata.id}>
<a className="navItem" href={this.getLink(linkMetadata)}>
{this.getLocalizedString(linkMetadata)}
</a>
</li>
);
};
render() {
return (
<nav className="toc">
<div className="toggleNav">
<section className="navWrapper wrapper">
<div className="navBreadcrumb wrapper">
<div className="navToggle" id="navToggler">
<div className="hamburger-menu">
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</div>
<h2>
<i>›</i>
<span>
{this.getLocalizedCategoryString(
this.props.current.subcategory ||
this.props.current.category,
)}
</span>
</h2>
{siteConfig.onPageNav === 'separate' && (
<div className="tocToggler" id="tocToggler">
<i className="icon-toc" />
</div>
)}
</div>
<div className="navGroups">
{this.props.contents.map(this.renderCategory)}
</div>
</section>
</div>
<script
dangerouslySetInnerHTML={{
__html: `
var coll = document.getElementsByClassName('collapsible');
var checkActiveCategory = true;
for (var i = 0; i < coll.length; i++) {
var links = coll[i].nextElementSibling.getElementsByTagName('*');
if (checkActiveCategory){
for (var j = 0; j < links.length; j++) {
if (links[j].classList.contains('navListItemActive')){
coll[i].nextElementSibling.classList.toggle('hide');
coll[i].childNodes[1].classList.toggle('rotate');
checkActiveCategory = false;
break;
}
}
}
coll[i].addEventListener('click', function() {
var arrow = this.childNodes[1];
arrow.classList.toggle('rotate');
var content = this.nextElementSibling;
content.classList.toggle('hide');
});
}
document.addEventListener('DOMContentLoaded', function() {
createToggler('#navToggler', '#docsNav', 'docsSliderActive');
createToggler('#tocToggler', 'body', 'tocActive');
var headings = document.querySelector('.toc-headings');
headings && headings.addEventListener('click', function(event) {
var el = event.target;
while(el !== headings){
if (el.tagName === 'A') {
document.body.classList.remove('tocActive');
break;
} else{
el = el.parentNode;
}
}
}, false);
function createToggler(togglerSelector, targetSelector, className) {
var toggler = document.querySelector(togglerSelector);
var target = document.querySelector(targetSelector);
if (!toggler) {
return;
}
toggler.onclick = function(event) {
event.preventDefault();
target.classList.toggle(className);
};
}
});
`,
}}
/>
</nav>
);
}
}
SideNav.defaultProps = {
collapsible: false,
contents: [],
};
module.exports = SideNav;