mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-30 10:48:05 +02:00
Add active link classes to site navigation menu (#463)
This commit is contained in:
parent
4ea8158c0c
commit
48ee457ec9
9 changed files with 66 additions and 15 deletions
|
@ -158,3 +158,15 @@ headerLinks: [
|
||||||
{ doc: 'bar', label: 'Bar' },
|
{ doc: 'bar', label: 'Bar' },
|
||||||
],
|
],
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Active Links In Site Navigation Bar
|
||||||
|
|
||||||
|
The links in the top navigation bar get `siteNavItemActive` and `siteNavGroupActive` class names to allow you to style the currently active link different from the others. `siteNavItemActive` is applied when there's an exact match between the navigation link and the currently displayed web page.
|
||||||
|
|
||||||
|
> This does not include links of type `href` which are meant for external links only. If you manually set an `href` in your headerLinks to an internal page, document, or blog post, it will not get the `siteNavItemActive` class even if that page is being displayed.
|
||||||
|
|
||||||
|
`siteNavGroupActive` will be added to these links:
|
||||||
|
* `doc` links that belong to the same sidebar as the currently displayed document
|
||||||
|
* The blog link when a blog post, or the blog listing page is being displayed
|
||||||
|
|
||||||
|
These are two separate class names so you can have the active styles applied to either exact matches only or a bit more broadly for docs that belong together. If you don't want to make this distinction you can add both classes to the same css rule.
|
||||||
|
|
|
@ -26,7 +26,11 @@ class BlogPageLayout extends React.Component {
|
||||||
const perPage = this.props.metadata.perPage;
|
const perPage = this.props.metadata.perPage;
|
||||||
const page = this.props.metadata.page;
|
const page = this.props.metadata.page;
|
||||||
return (
|
return (
|
||||||
<Site title="Blog" language="en" config={this.props.config}>
|
<Site
|
||||||
|
title="Blog"
|
||||||
|
language="en"
|
||||||
|
config={this.props.config}
|
||||||
|
metadata={{blog: true, blogListing: true}}>
|
||||||
<div className="docMainWrapper wrapper">
|
<div className="docMainWrapper wrapper">
|
||||||
<BlogSidebar
|
<BlogSidebar
|
||||||
language={this.props.language}
|
language={this.props.language}
|
||||||
|
|
|
@ -79,7 +79,8 @@ class BlogPostLayout extends React.Component {
|
||||||
title={this.props.metadata.title}
|
title={this.props.metadata.title}
|
||||||
language={'en'}
|
language={'en'}
|
||||||
description={this.getDescription()}
|
description={this.getDescription()}
|
||||||
config={this.props.config}>
|
config={this.props.config}
|
||||||
|
metadata={{blog: true}}>
|
||||||
<div className="docMainWrapper wrapper">
|
<div className="docMainWrapper wrapper">
|
||||||
<BlogSidebar
|
<BlogSidebar
|
||||||
language={'en'}
|
language={'en'}
|
||||||
|
|
|
@ -36,7 +36,8 @@ class DocsLayout extends React.Component {
|
||||||
}
|
}
|
||||||
description={content.trim().split('\n')[0]}
|
description={content.trim().split('\n')[0]}
|
||||||
language={metadata.language}
|
language={metadata.language}
|
||||||
version={metadata.version}>
|
version={metadata.version}
|
||||||
|
metadata={metadata}>
|
||||||
<div className="docMainWrapper wrapper">
|
<div className="docMainWrapper wrapper">
|
||||||
<DocsSidebar metadata={metadata} />
|
<DocsSidebar metadata={metadata} />
|
||||||
<Container className="mainContainer">
|
<Container className="mainContainer">
|
||||||
|
|
|
@ -75,6 +75,7 @@ class Site extends React.Component {
|
||||||
title={this.props.config.title}
|
title={this.props.config.title}
|
||||||
language={this.props.language}
|
language={this.props.language}
|
||||||
version={this.props.version}
|
version={this.props.version}
|
||||||
|
current={this.props.metadata}
|
||||||
/>
|
/>
|
||||||
<div className="navPusher">
|
<div className="navPusher">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|
|
@ -9,6 +9,7 @@ const CWD = process.cwd();
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const classNames = require('classnames');
|
||||||
const siteConfig = require(CWD + '/siteConfig.js');
|
const siteConfig = require(CWD + '/siteConfig.js');
|
||||||
const translation = require('../../server/translation.js');
|
const translation = require('../../server/translation.js');
|
||||||
const env = require('../../server/env.js');
|
const env = require('../../server/env.js');
|
||||||
|
@ -97,6 +98,8 @@ class HeaderNav extends React.Component {
|
||||||
// function to generate each header link, used with each object in siteConfig.headerLinks
|
// function to generate each header link, used with each object in siteConfig.headerLinks
|
||||||
makeLinks(link) {
|
makeLinks(link) {
|
||||||
let href;
|
let href;
|
||||||
|
let docItemActive = false;
|
||||||
|
let docGroupActive = false;
|
||||||
if (link.search && this.props.config.algolia) {
|
if (link.search && this.props.config.algolia) {
|
||||||
// return algolia search bar
|
// return algolia search bar
|
||||||
return (
|
return (
|
||||||
|
@ -153,6 +156,10 @@ class HeaderNav extends React.Component {
|
||||||
throw new Error(errorStr);
|
throw new Error(errorStr);
|
||||||
}
|
}
|
||||||
href = this.props.config.baseUrl + Metadata[id].permalink;
|
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) {
|
} else if (link.page) {
|
||||||
// set link to page with current page's language if appropriate
|
// set link to page with current page's language if appropriate
|
||||||
const language = this.props.language || '';
|
const language = this.props.language || '';
|
||||||
|
@ -172,8 +179,16 @@ class HeaderNav extends React.Component {
|
||||||
// set link to blog url
|
// set link to blog url
|
||||||
href = this.props.baseUrl + 'blog';
|
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 (
|
return (
|
||||||
<li key={link.label + 'page'}>
|
<li key={link.label + 'page'} className={itemClasses}>
|
||||||
<a href={href} target={link.external ? '_blank' : '_self'}>
|
<a href={href} target={link.external ? '_blank' : '_self'}>
|
||||||
{translation[this.props.language]
|
{translation[this.props.language]
|
||||||
? translation[this.props.language]['localized-strings'][link.label]
|
? translation[this.props.language]['localized-strings'][link.label]
|
||||||
|
@ -279,4 +294,8 @@ class HeaderNav extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HeaderNav.defaultProps = {
|
||||||
|
current: {},
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = HeaderNav;
|
module.exports = HeaderNav;
|
||||||
|
|
|
@ -407,6 +407,8 @@ function execute() {
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
// render .js files to strings
|
// render .js files to strings
|
||||||
if (file.match(/\.js$/)) {
|
if (file.match(/\.js$/)) {
|
||||||
|
const pageID = path.basename(file, '.js');
|
||||||
|
|
||||||
// make temp file for sake of require paths
|
// make temp file for sake of require paths
|
||||||
const parts = file.split('pages');
|
const parts = file.split('pages');
|
||||||
let tempFile = join(__dirname, '..', 'pages', parts[1]);
|
let tempFile = join(__dirname, '..', 'pages', parts[1]);
|
||||||
|
@ -439,7 +441,10 @@ function execute() {
|
||||||
}
|
}
|
||||||
translate.setLanguage(language);
|
translate.setLanguage(language);
|
||||||
const str = renderToStaticMarkup(
|
const str = renderToStaticMarkup(
|
||||||
<Site language={language} config={siteConfig}>
|
<Site
|
||||||
|
language={language}
|
||||||
|
config={siteConfig}
|
||||||
|
metadata={{id: pageID}}>
|
||||||
<ReactComp language={language} />
|
<ReactComp language={language} />
|
||||||
</Site>
|
</Site>
|
||||||
);
|
);
|
||||||
|
@ -454,7 +459,7 @@ function execute() {
|
||||||
let language = env.translation.enabled ? 'en' : '';
|
let language = env.translation.enabled ? 'en' : '';
|
||||||
translate.setLanguage(language);
|
translate.setLanguage(language);
|
||||||
const str = renderToStaticMarkup(
|
const str = renderToStaticMarkup(
|
||||||
<Site language={language} config={siteConfig}>
|
<Site language={language} config={siteConfig} metadata={{id: pageID}}>
|
||||||
<ReactComp language={language} />
|
<ReactComp language={language} />
|
||||||
</Site>
|
</Site>
|
||||||
);
|
);
|
||||||
|
@ -464,7 +469,7 @@ function execute() {
|
||||||
let language = env.translation.enabled ? 'en' : '';
|
let language = env.translation.enabled ? 'en' : '';
|
||||||
translate.setLanguage(language);
|
translate.setLanguage(language);
|
||||||
const str = renderToStaticMarkup(
|
const str = renderToStaticMarkup(
|
||||||
<Site language={language} config={siteConfig}>
|
<Site language={language} config={siteConfig} metadata={{id: pageID}}>
|
||||||
<ReactComp language={language} />
|
<ReactComp language={language} />
|
||||||
</Site>
|
</Site>
|
||||||
);
|
);
|
||||||
|
@ -472,10 +477,11 @@ function execute() {
|
||||||
}
|
}
|
||||||
fs.removeSync(tempFile);
|
fs.removeSync(tempFile);
|
||||||
} else if (siteConfig.wrapPagesHTML && file.match(/\.html$/)) {
|
} else if (siteConfig.wrapPagesHTML && file.match(/\.html$/)) {
|
||||||
|
const pageID = path.basename(file, '.html');
|
||||||
const parts = file.split('pages');
|
const parts = file.split('pages');
|
||||||
const targetFile = join(buildDir, parts[1]);
|
const targetFile = join(buildDir, parts[1]);
|
||||||
const str = renderToStaticMarkup(
|
const str = renderToStaticMarkup(
|
||||||
<Site language="en" config={siteConfig}>
|
<Site language="en" config={siteConfig} metadata={{id: pageID}}>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: fs.readFileSync(file, {encoding: 'utf8'}),
|
__html: fs.readFileSync(file, {encoding: 'utf8'}),
|
||||||
|
|
|
@ -366,7 +366,10 @@ function execute(port) {
|
||||||
removeModuleAndChildrenFromCache('../core/Site.js');
|
removeModuleAndChildrenFromCache('../core/Site.js');
|
||||||
const Site = require('../core/Site.js');
|
const Site = require('../core/Site.js');
|
||||||
const str = renderToStaticMarkup(
|
const str = renderToStaticMarkup(
|
||||||
<Site language="en" config={siteConfig}>
|
<Site
|
||||||
|
language="en"
|
||||||
|
config={siteConfig}
|
||||||
|
metadata={{id: path.basename(htmlFile, '.html')}}>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: fs.readFileSync(htmlFile, {encoding: 'utf8'}),
|
__html: fs.readFileSync(htmlFile, {encoding: 'utf8'}),
|
||||||
|
@ -434,7 +437,10 @@ function execute(port) {
|
||||||
const Site = require('../core/Site.js');
|
const Site = require('../core/Site.js');
|
||||||
translate.setLanguage(language);
|
translate.setLanguage(language);
|
||||||
const str = renderToStaticMarkup(
|
const str = renderToStaticMarkup(
|
||||||
<Site language={language} config={siteConfig}>
|
<Site
|
||||||
|
language={language}
|
||||||
|
config={siteConfig}
|
||||||
|
metadata={{id: path.basename(userFile, '.js')}}>
|
||||||
<ReactComp language={language} />
|
<ReactComp language={language} />
|
||||||
</Site>
|
</Site>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1009,7 +1009,9 @@ pre code {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
.navigationSlider .slidingNav ul li a:focus,
|
.navigationSlider .slidingNav ul li a:focus,
|
||||||
.navigationSlider .slidingNav ul li a:hover {
|
.navigationSlider .slidingNav ul li a:hover,
|
||||||
|
.navigationSlider .slidingNav ul li.siteNavItemActive a,
|
||||||
|
.navigationSlider .slidingNav ul li.siteNavGroupActive a {
|
||||||
background: $primaryColor;
|
background: $primaryColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,10 +1197,9 @@ ul#languages li {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
.navigationSlider .slidingNav ul li a:hover {
|
.navigationSlider .slidingNav ul li a:hover,
|
||||||
color: #fff;
|
.navigationSlider .slidingNav ul li.siteNavItemActive a,
|
||||||
}
|
.navigationSlider .slidingNav ul li.siteNavGroupActive a {
|
||||||
.navigationSlider .slidingNav ul li.navItemActive a {
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue