From 48ee457ec98b728343196362d5d42c0dc3d1cff9 Mon Sep 17 00:00:00 2001 From: Elian Ibaj Date: Sun, 4 Mar 2018 17:53:31 +0100 Subject: [PATCH] Add active link classes to site navigation menu (#463) --- docs/guides-navigation.md | 12 ++++++++++++ lib/core/BlogPageLayout.js | 6 +++++- lib/core/BlogPostLayout.js | 3 ++- lib/core/DocsLayout.js | 3 ++- lib/core/Site.js | 1 + lib/core/nav/HeaderNav.js | 21 ++++++++++++++++++++- lib/server/generate.js | 14 ++++++++++---- lib/server/server.js | 10 ++++++++-- lib/static/css/main.css | 11 ++++++----- 9 files changed, 66 insertions(+), 15 deletions(-) diff --git a/docs/guides-navigation.md b/docs/guides-navigation.md index 010077ab9c92..1c083f277194 100644 --- a/docs/guides-navigation.md +++ b/docs/guides-navigation.md @@ -158,3 +158,15 @@ headerLinks: [ { 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. diff --git a/lib/core/BlogPageLayout.js b/lib/core/BlogPageLayout.js index df5e43ab7984..77cf2116de26 100644 --- a/lib/core/BlogPageLayout.js +++ b/lib/core/BlogPageLayout.js @@ -26,7 +26,11 @@ class BlogPageLayout extends React.Component { const perPage = this.props.metadata.perPage; const page = this.props.metadata.page; return ( - +
+ config={this.props.config} + metadata={{blog: true}}>
+ version={metadata.version} + metadata={metadata}>
diff --git a/lib/core/Site.js b/lib/core/Site.js index 2a0cbed81631..c4feec0a9844 100644 --- a/lib/core/Site.js +++ b/lib/core/Site.js @@ -75,6 +75,7 @@ class Site extends React.Component { title={this.props.config.title} language={this.props.language} version={this.props.version} + current={this.props.metadata} />
{this.props.children} diff --git a/lib/core/nav/HeaderNav.js b/lib/core/nav/HeaderNav.js index 253ad0cfe7a4..fb59101b5bbf 100644 --- a/lib/core/nav/HeaderNav.js +++ b/lib/core/nav/HeaderNav.js @@ -9,6 +9,7 @@ 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'); @@ -97,6 +98,8 @@ class HeaderNav extends React.Component { // 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 ( @@ -153,6 +156,10 @@ class HeaderNav extends React.Component { 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 || ''; @@ -172,8 +179,16 @@ class HeaderNav extends React.Component { // 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 ( -
  • +
  • {translation[this.props.language] ? translation[this.props.language]['localized-strings'][link.label] @@ -279,4 +294,8 @@ class HeaderNav extends React.Component { } } +HeaderNav.defaultProps = { + current: {}, +}; + module.exports = HeaderNav; diff --git a/lib/server/generate.js b/lib/server/generate.js index d05c5b70da7f..cd0531a03734 100644 --- a/lib/server/generate.js +++ b/lib/server/generate.js @@ -407,6 +407,8 @@ function execute() { files.forEach(file => { // render .js files to strings if (file.match(/\.js$/)) { + const pageID = path.basename(file, '.js'); + // make temp file for sake of require paths const parts = file.split('pages'); let tempFile = join(__dirname, '..', 'pages', parts[1]); @@ -439,7 +441,10 @@ function execute() { } translate.setLanguage(language); const str = renderToStaticMarkup( - + ); @@ -454,7 +459,7 @@ function execute() { let language = env.translation.enabled ? 'en' : ''; translate.setLanguage(language); const str = renderToStaticMarkup( - + ); @@ -464,7 +469,7 @@ function execute() { let language = env.translation.enabled ? 'en' : ''; translate.setLanguage(language); const str = renderToStaticMarkup( - + ); @@ -472,10 +477,11 @@ function execute() { } fs.removeSync(tempFile); } else if (siteConfig.wrapPagesHTML && file.match(/\.html$/)) { + const pageID = path.basename(file, '.html'); const parts = file.split('pages'); const targetFile = join(buildDir, parts[1]); const str = renderToStaticMarkup( - +
    +
    + ); diff --git a/lib/static/css/main.css b/lib/static/css/main.css index 3dd560e23300..65e1f8d5c305 100644 --- a/lib/static/css/main.css +++ b/lib/static/css/main.css @@ -1009,7 +1009,9 @@ pre code { font-size: 0.9em; } .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; } @@ -1195,10 +1197,9 @@ ul#languages li { height: 32px; font-size: 1em; } - .navigationSlider .slidingNav ul li a:hover { - color: #fff; - } - .navigationSlider .slidingNav ul li.navItemActive a { + .navigationSlider .slidingNav ul li a:hover, + .navigationSlider .slidingNav ul li.siteNavItemActive a, + .navigationSlider .slidingNav ul li.siteNavGroupActive a { color: #fff; } }