mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-01 03:08:17 +02:00
323 lines
10 KiB
JavaScript
323 lines
10 KiB
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 CWD = process.cwd();
|
|
|
|
const React = require('react');
|
|
const fs = require('fs');
|
|
const classNames = require('classnames');
|
|
const siteConfig = require(CWD + '/siteConfig.js');
|
|
const translation = require('../../server/translation.js');
|
|
const env = require('../../server/env.js');
|
|
|
|
const translate = require('../../server/translate.js').translate;
|
|
const setLanguage = require('../../server/translate.js').setLanguage;
|
|
|
|
const readMetadata = require('../../server/readMetadata.js');
|
|
readMetadata.generateMetadataDocs();
|
|
const Metadata = require('../metadata.js');
|
|
const utils = require('../utils.js');
|
|
|
|
// language dropdown nav item for when translations are enabled
|
|
class LanguageDropDown extends React.Component {
|
|
render() {
|
|
setLanguage(this.props.language || 'en');
|
|
let helpTranslateString = translate(
|
|
'Help Translate|recruit community translators for your project'
|
|
);
|
|
// add all enabled languages to dropdown
|
|
const enabledLanguages = env.translation
|
|
.enabledLanguages()
|
|
.filter(lang => lang.tag !== this.props.language)
|
|
.map(lang => (
|
|
<li key={lang.tag}>
|
|
<a href={siteConfig.baseUrl + lang.tag}>{lang.name}</a>
|
|
</li>
|
|
));
|
|
// if no languages are enabled besides English, return null
|
|
if (enabledLanguages.length < 1) {
|
|
return null;
|
|
}
|
|
|
|
// Get the current language full name for display in the header nav
|
|
const currentLanguage = env.translation
|
|
.enabledLanguages()
|
|
.filter(lang => lang.tag === this.props.language)
|
|
.map(lang => lang.name);
|
|
|
|
// add Crowdin project recruiting link
|
|
if (siteConfig.translationRecruitingLink) {
|
|
enabledLanguages.push(
|
|
<li key="recruiting">
|
|
<a
|
|
href={siteConfig.translationRecruitingLink}
|
|
target="_blank"
|
|
rel="noreferrer noopener">
|
|
{helpTranslateString}
|
|
</a>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<span>
|
|
<li key="languages">
|
|
<a id="languages-menu" href="#">
|
|
<img
|
|
className="languages-icon"
|
|
src={this.props.baseUrl + 'img/language.svg'}
|
|
/>
|
|
{currentLanguage}
|
|
</a>
|
|
<div id="languages-dropdown" className="hide">
|
|
<ul id="languages-dropdown-items">{enabledLanguages}</ul>
|
|
</div>
|
|
</li>
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
const languagesMenuItem = document.getElementById("languages-menu");
|
|
const languagesDropDown = document.getElementById("languages-dropdown");
|
|
languagesMenuItem.addEventListener("click", function(){
|
|
if(languagesDropDown.className == "hide") {
|
|
languagesDropDown.className = "visible";
|
|
} else {
|
|
languagesDropDown.className = "hide";
|
|
}
|
|
});
|
|
`,
|
|
}}
|
|
/>
|
|
</span>
|
|
);
|
|
}
|
|
}
|
|
|
|
// header navbar used by all pages generated with docusaurus
|
|
class HeaderNav extends React.Component {
|
|
constructor() {
|
|
super();
|
|
this.state = {
|
|
slideoutActive: false,
|
|
};
|
|
}
|
|
// function to generate each header link, used with each object in siteConfig.headerLinks
|
|
makeLinks(link) {
|
|
let href;
|
|
let docItemActive = false;
|
|
let docGroupActive = false;
|
|
if (link.search && this.props.config.algolia) {
|
|
// return algolia search bar
|
|
return (
|
|
<li className="navSearchWrapper reactNavSearchWrapper" key="search">
|
|
<input
|
|
id="search_input_react"
|
|
type="text"
|
|
placeholder="Search"
|
|
title="Search"
|
|
/>
|
|
</li>
|
|
);
|
|
} else if (link.languages) {
|
|
if (
|
|
env.translation.enabled &&
|
|
env.translation.enabledLanguages().length > 1
|
|
) {
|
|
return (
|
|
<LanguageDropDown
|
|
baseUrl={this.props.baseUrl}
|
|
language={this.props.language}
|
|
key="languagedropdown"
|
|
/>
|
|
);
|
|
} else {
|
|
return null;
|
|
}
|
|
} else if (link.doc) {
|
|
// set link to document with current page's language/version
|
|
const langPart = env.translation.enabled
|
|
? (this.props.language || 'en') + '-'
|
|
: '';
|
|
const versionPart =
|
|
env.versioning.enabled && this.props.version !== 'next'
|
|
? 'version-' +
|
|
(this.props.version || env.versioning.defaultVersion) +
|
|
'-'
|
|
: '';
|
|
const id = langPart + versionPart + link.doc;
|
|
if (!Metadata[id]) {
|
|
let errorStr =
|
|
"Processing the following `doc` field in `headerLinks` within `siteConfig.js`: '" +
|
|
link.doc +
|
|
"'";
|
|
if (id === link.doc) {
|
|
errorStr +=
|
|
' It looks like there is no document with that id that exists in your docs directory. Please double check the spelling of your `doc` field and the `id` fields of your docs.';
|
|
} else {
|
|
errorStr +=
|
|
'. Check the spelling of your `doc` field. If that seems sane, and a document in your docs folder exists with that `id` value, \nthen this is likely a bug in Docusaurus.' +
|
|
' Docusaurus thinks one or both of translations (currently set to: ' +
|
|
env.translation.enabled +
|
|
') or versioning (currently set to: ' +
|
|
env.versioning.enabled +
|
|
") is enabled when maybe they should not be. \nThus my internal id for this doc is: '" +
|
|
id +
|
|
"'. Please file an issue for this possible bug on GitHub.";
|
|
}
|
|
throw new Error(errorStr);
|
|
}
|
|
href =
|
|
this.props.config.baseUrl +
|
|
utils.getPath(Metadata[id].permalink, this.props.config.cleanUrl);
|
|
|
|
const {id: currentID, sidebar} = this.props.current;
|
|
docItemActive = currentID && currentID === id;
|
|
docGroupActive = sidebar && sidebar === Metadata[id].sidebar;
|
|
} else if (link.page) {
|
|
// set link to page with current page's language if appropriate
|
|
const language = this.props.language || '';
|
|
const extension = siteConfig.cleanUrl ? '' : '.html';
|
|
if (fs.existsSync(CWD + '/pages/en/' + link.page + '.js')) {
|
|
href =
|
|
siteConfig.baseUrl +
|
|
(language ? language + '/' : '') +
|
|
link.page +
|
|
extension;
|
|
} else {
|
|
href = siteConfig.baseUrl + link.page + extension;
|
|
}
|
|
} else if (link.href) {
|
|
// set link to specified href
|
|
href = link.href;
|
|
} else if (link.blog) {
|
|
// set link to blog url
|
|
href = this.props.baseUrl + 'blog';
|
|
}
|
|
const itemClasses = classNames({
|
|
siteNavGroupActive:
|
|
(link.doc && docGroupActive) || (link.blog && this.props.current.blog),
|
|
siteNavItemActive:
|
|
docItemActive ||
|
|
(link.blog && this.props.current.blogListing) ||
|
|
(link.page && link.page === this.props.current.id),
|
|
});
|
|
return (
|
|
<li key={link.label + 'page'} className={itemClasses}>
|
|
<a href={href} target={link.external ? '_blank' : '_self'}>
|
|
{translation[this.props.language]
|
|
? translation[this.props.language]['localized-strings'][link.label]
|
|
: link.label}
|
|
</a>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const headerClass = siteConfig.headerIcon
|
|
? 'headerTitleWithLogo'
|
|
: 'headerTitle';
|
|
const versionsLink =
|
|
this.props.baseUrl +
|
|
(env.translation.enabled
|
|
? this.props.language + '/versions.html'
|
|
: 'versions.html');
|
|
return (
|
|
<div className="fixedHeaderContainer">
|
|
<div className="headerWrapper wrapper">
|
|
<header>
|
|
<a
|
|
href={
|
|
this.props.baseUrl +
|
|
(env.translation.enabled ? this.props.language : '')
|
|
}>
|
|
{siteConfig.headerIcon && (
|
|
<img
|
|
className="logo"
|
|
src={this.props.baseUrl + siteConfig.headerIcon}
|
|
alt={siteConfig.title}
|
|
/>
|
|
)}
|
|
{!this.props.config.disableHeaderTitle && (
|
|
<h2 className={headerClass}>{this.props.title}</h2>
|
|
)}
|
|
</a>
|
|
{env.versioning.enabled && (
|
|
<a href={versionsLink}>
|
|
<h3>{this.props.version || env.versioning.defaultVersion}</h3>
|
|
</a>
|
|
)}
|
|
{this.renderResponsiveNav()}
|
|
</header>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderResponsiveNav() {
|
|
const headerLinks = this.props.config.headerLinks;
|
|
// add language drop down to end if location not specified
|
|
let languages = false;
|
|
headerLinks.forEach(link => {
|
|
if (link.languages) {
|
|
languages = true;
|
|
}
|
|
});
|
|
if (!languages) {
|
|
headerLinks.push({languages: true});
|
|
}
|
|
let search = false;
|
|
headerLinks.forEach(link => {
|
|
if (
|
|
link.doc &&
|
|
!fs.existsSync(CWD + '/../' + readMetadata.getDocsPath() + '/')
|
|
) {
|
|
throw new Error(
|
|
"You have 'doc' in your headerLinks, but no '" +
|
|
readMetadata.getDocsPath() +
|
|
"' folder exists one level up from " +
|
|
"'website' folder. Did you run `docusaurus-init` or `npm run examples`? If so, " +
|
|
"make sure you rename 'docs-examples-from-docusaurus' to 'docs'."
|
|
);
|
|
}
|
|
if (link.blog && !fs.existsSync(CWD + '/blog/')) {
|
|
throw new Error(
|
|
"You have 'blog' in your headerLinks, but no 'blog' folder exists in your " +
|
|
"'website' folder. Did you run `docusaurus-init` or `npm run examples`? If so, " +
|
|
"make sure you rename 'blog-examples-from-docusaurus' to 'blog'."
|
|
);
|
|
}
|
|
if (link.page && !fs.existsSync(CWD + '/pages/')) {
|
|
throw new Error(
|
|
"You have 'page' in your headerLinks, but no 'pages' folder exists in your " +
|
|
"'website' folder."
|
|
);
|
|
}
|
|
// We will add search bar to end if location not specified
|
|
if (link.search) {
|
|
search = true;
|
|
}
|
|
});
|
|
if (!search && this.props.config.algolia) {
|
|
headerLinks.push({search: true});
|
|
}
|
|
return (
|
|
<div className="navigationWrapper navigationSlider">
|
|
<nav className="slidingNav">
|
|
<ul className="nav-site nav-site-internal">
|
|
{headerLinks.map(this.makeLinks, this)}
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
HeaderNav.defaultProps = {
|
|
current: {},
|
|
};
|
|
|
|
module.exports = HeaderNav;
|