/** * 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'); // language dropdown nav item for when translations are enabled class LanguageDropDown extends React.Component { render() { let currentLanguage = 'English'; 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 !== 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; } // 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.latestVersion) + '-' : ''; 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 + Metadata[id].permalink; 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 || ''; if (fs.existsSync(CWD + '/pages/en/' + link.page + '.js')) { href = siteConfig.baseUrl + (language ? language + '/' : '') + link.page + '.html'; } else { href = siteConfig.baseUrl + link.page + '.html'; } } 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 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="headerTitle">{this.props.title}</h2> )} </a> {env.versioning.enabled && ( <a href={versionsLink}> <h3>{this.props.version || env.versioning.latestVersion}</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;