From 94270569a5bc03b8e80c5ecb655f8a751cf0420d Mon Sep 17 00:00:00 2001 From: jxom Date: Sat, 7 Oct 2017 18:33:53 +1100 Subject: [PATCH 1/5] Tweak sidebar to update navigation highlight on scroll --- content/tutorial/nav.yml | 2 +- src/templates/components/Sidebar/Section.js | 227 +++++++++++++------- src/utils/createLink.js | 15 +- 3 files changed, 162 insertions(+), 82 deletions(-) diff --git a/content/tutorial/nav.yml b/content/tutorial/nav.yml index f3570cc9..adc5cf3a 100644 --- a/content/tutorial/nav.yml +++ b/content/tutorial/nav.yml @@ -1,6 +1,6 @@ - title: Tutorial items: - - id: tutorial + - id: before-we-start title: Before We Start href: /tutorial/tutorial.html#before-we-start forceInternal: true diff --git a/src/templates/components/Sidebar/Section.js b/src/templates/components/Sidebar/Section.js index e212f03e..a264ebfd 100644 --- a/src/templates/components/Sidebar/Section.js +++ b/src/templates/components/Sidebar/Section.js @@ -9,88 +9,161 @@ 'use strict'; -import React from 'react'; +import React, {Component} from 'react'; import {colors, media} from 'theme'; import MetaTitle from '../MetaTitle'; import ChevronSvg from '../ChevronSvg'; -// TODO Update isActive link as document scrolls past anchor tags -// Maybe used 'hashchange' along with 'scroll' to set/update active links - -const Section = ({ - createLink, - isActive, - location, - onLinkClick, - onSectionTitleClick, - section, -}) => ( -
- - {section.title} - - - +
+ ); + } +} + +const _getItemIds = items => + items + .map(item => { + let subItemIds = []; + if (item.subitems) { + subItemIds = item.subitems.map(subitem => subitem.id); + } + return [item.id, ...subItemIds]; + }) + .reduce((prev, current) => prev.concat(current)); + +const _getElementTopOffsetsById = ids => + ids + .map(id => { + const element = document.getElementById(id); + if (!element) { + return null; + } + return { + id, + offsetTop: element.offsetTop, + }; + }) + .filter(item => item); export default Section; diff --git a/src/utils/createLink.js b/src/utils/createLink.js index 88bac825..a2871d5f 100644 --- a/src/utils/createLink.js +++ b/src/utils/createLink.js @@ -65,15 +65,22 @@ const createLinkDocs = ({item, location, section}) => { ); }; -const createLinkTutorial = ({item, location, onLinkClick, section}) => { - const isActive = isItemActive(location, item); +const createLinkTutorial = ({ + item, + location, + onLinkClick, + section, + isActive, +}) => { + const active = + typeof isActive === 'boolean' ? isActive : isItemActive(location, item); return ( - {isActive && } + {active && } {item.title} ); From 63280cca1d4c6635a0d25c118997b188853c2c02 Mon Sep 17 00:00:00 2001 From: jxom Date: Sat, 7 Oct 2017 18:41:26 +1100 Subject: [PATCH 2/5] Make createLinkBlog and createLinkDocs consistent with createLinkTutorial --- src/utils/createLink.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/utils/createLink.js b/src/utils/createLink.js index a2871d5f..2d1b1e99 100644 --- a/src/utils/createLink.js +++ b/src/utils/createLink.js @@ -18,12 +18,13 @@ import isItemActive from 'utils/isItemActive'; import slugify from 'utils/slugify'; import {colors, media} from 'theme'; -const createLinkBlog = ({item, location, section}) => { - const isActive = isItemActive(location, item); +const createLinkBlog = ({item, location, section, isActive}) => { + const active = + typeof isActive === 'boolean' ? isActive : isItemActive(location, item); return ( - - {isActive && } + + {active && } {item.title} ); @@ -52,14 +53,15 @@ const createLinkCommunity = ({item, location, section}) => { }); }; -const createLinkDocs = ({item, location, section}) => { - const isActive = isItemActive(location, item); +const createLinkDocs = ({item, location, section, isActive}) => { + const active = + typeof isActive === 'boolean' ? isActive : isItemActive(location, item); return ( - {isActive && } + {active && } {item.title} ); From e9d0884a430641475e4025a57f515ea7cb394211 Mon Sep 17 00:00:00 2001 From: jxom Date: Sun, 8 Oct 2017 11:33:36 +1100 Subject: [PATCH 3/5] PR Changes --- src/components/MarkdownPage/MarkdownPage.js | 7 +- .../components/Sidebar/ScrollSyncSection.js | 108 +++++++++ src/templates/components/Sidebar/Section.js | 228 ++++++------------ src/templates/components/Sidebar/Sidebar.js | 9 +- src/templates/tutorial.js | 1 + src/utils/createLink.js | 37 +-- src/utils/isItemActive.js | 2 +- 7 files changed, 210 insertions(+), 182 deletions(-) create mode 100644 src/templates/components/Sidebar/ScrollSyncSection.js diff --git a/src/components/MarkdownPage/MarkdownPage.js b/src/components/MarkdownPage/MarkdownPage.js index cb4249a2..f4bc96ef 100644 --- a/src/components/MarkdownPage/MarkdownPage.js +++ b/src/components/MarkdownPage/MarkdownPage.js @@ -25,7 +25,8 @@ import createOgUrl from 'utils/createOgUrl'; const MarkdownPage = ({ authors, createLink, - date, + date, + enableScrollSync, ogDescription, location, markdownRemark, @@ -98,6 +99,7 @@ const MarkdownPage = ({
{ + const nextItemTopOffset = itemTopOffsets[i + 1]; + if (nextItemTopOffset) { + return ( + window.scrollY >= itemTopOffset.offsetTop && + window.scrollY < nextItemTopOffset.offsetTop + ); + } + return window.scrollY >= itemTopOffset.offsetTop; + }); + this.setState({ + activeItemId: item ? item.id : '', + }); + } + + render() { + const {activeItemId} = this.state; + return ( +
+ ); + } +} + +const _getItemIds = items => + items + .map(item => { + let subItemIds = []; + if (item.subitems) { + subItemIds = item.subitems.map(subitem => subitem.id); + } + return [item.id, ...subItemIds]; + }) + .reduce((prev, current) => prev.concat(current)); + +const _getElementTopOffsetsById = ids => + ids + .map(id => { + const element = document.getElementById(id); + if (!element) { + return null; + } + return { + id, + offsetTop: element.offsetTop, + }; + }) + .filter(item => item); + +export default ScrollSyncSection; diff --git a/src/templates/components/Sidebar/Section.js b/src/templates/components/Sidebar/Section.js index a264ebfd..99215c66 100644 --- a/src/templates/components/Sidebar/Section.js +++ b/src/templates/components/Sidebar/Section.js @@ -9,161 +9,89 @@ 'use strict'; -import React, {Component} from 'react'; import {colors, media} from 'theme'; +import isItemActive from 'utils/isItemActive'; import MetaTitle from '../MetaTitle'; import ChevronSvg from '../ChevronSvg'; -class Section extends Component { - constructor(props, context) { - super(props, context); - - this.state = { - activeItemId: null, - itemTopOffsets: [], - }; - - this.handleScroll = this.handleScroll.bind(this); - } - - componentDidMount() { - const {section} = this.props; - - const itemIds = _getItemIds(section.items); - this.setState({ - itemTopOffsets: _getElementTopOffsetsById(itemIds), - }); - - window.addEventListener('scroll', this.handleScroll); - } - - componentWillUnmount() { - window.removeEventListener('scroll', this.handleScroll); - } - - handleScroll() { - const {itemTopOffsets} = this.state; - const item = itemTopOffsets.find((itemTopOffset, i) => { - const nextItemTopOffset = itemTopOffsets[i + 1]; - if (nextItemTopOffset) { - return ( - window.scrollY >= itemTopOffset.offsetTop && - window.scrollY < nextItemTopOffset.offsetTop - ); - } - return window.scrollY >= itemTopOffset.offsetTop; - }); - this.setState({ - activeItemId: item ? item.id : null, - }); - } - - render() { - const { - createLink, - isActive, - location, - onLinkClick, - onSectionTitleClick, - section, - } = this.props; - const {activeItemId} = this.state; - return ( -
- - {section.title} - - -
    ( +
    + + {section.title} + + +
      + {section.items.map(item => ( +
    • - {section.items.map(item => ( -
    • - {createLink({ - item, - location, - onLinkClick, - section, - isActive: activeItemId === item.id, - })} - - {item.subitems && ( -
        - {item.subitems.map(subitem => ( -
      • - {createLink({ - item: subitem, - location, - onLinkClick, - section, - isActive: activeItemId === subitem.id, - })} -
      • - ))} -
      - )} -
    • - ))} -
    -
    - ); - } -} - -const _getItemIds = items => - items - .map(item => { - let subItemIds = []; - if (item.subitems) { - subItemIds = item.subitems.map(subitem => subitem.id); - } - return [item.id, ...subItemIds]; - }) - .reduce((prev, current) => prev.concat(current)); - -const _getElementTopOffsetsById = ids => - ids - .map(id => { - const element = document.getElementById(id); - if (!element) { - return null; - } - return { - id, - offsetTop: element.offsetTop, - }; - }) - .filter(item => item); + {createLink({ + isActive: isScrollSync ? activeItemId === item.id : isItemActive(location, item), + item, + location, + onLinkClick, + section, + })} + + {item.subitems && ( +
      + {item.subitems.map(subitem => ( +
    • + {createLink({ + isActive: isScrollSync ? activeItemId === subitem.id : isItemActive(location, subitem), + item: subitem, + location, + onLinkClick, + section, + })} +
    • + ))} +
    + )} + + ))} +
+
+); export default Section; diff --git a/src/templates/components/Sidebar/Sidebar.js b/src/templates/components/Sidebar/Sidebar.js index 830b23e4..5765a4c8 100644 --- a/src/templates/components/Sidebar/Sidebar.js +++ b/src/templates/components/Sidebar/Sidebar.js @@ -12,6 +12,7 @@ import React, {Component} from 'react'; import Flex from 'components/Flex'; import Section from './Section'; +import ScrollSyncSection from './ScrollSyncSection'; import {media} from 'theme'; class Sidebar extends Component { @@ -24,8 +25,10 @@ class Sidebar extends Component { } render() { - const {closeParentMenu, createLink, location, sectionList} = this.props; - const {activeSection} = this.state; + const {closeParentMenu, createLink, enableScrollSync, location, sectionList} = this.props; + const {activeSection} = this.state; + + const SectionComponent = enableScrollSync ? ScrollSyncSection : Section; return ( {sectionList.map((section, index) => ( -
{ return ( { - const active = - typeof isActive === 'boolean' ? isActive : isItemActive(location, item); - +const createLinkBlog = ({isActive, item, section}) => { return ( - - {active && } + + {isActive && } {item.title} ); }; -const createLinkCommunity = ({item, location, section}) => { +const createLinkCommunity = ({isActive, item, section}) => { if (item.href) { return ( @@ -47,42 +44,30 @@ const createLinkCommunity = ({item, location, section}) => { ); } return createLinkDocs({ + isActive, item, - location, section, }); }; -const createLinkDocs = ({item, location, section, isActive}) => { - const active = - typeof isActive === 'boolean' ? isActive : isItemActive(location, item); - +const createLinkDocs = ({isActive, item, section}) => { return ( - {active && } + {isActive && } {item.title} ); }; -const createLinkTutorial = ({ - item, - location, - onLinkClick, - section, - isActive, -}) => { - const active = - typeof isActive === 'boolean' ? isActive : isItemActive(location, item); - +const createLinkTutorial = ({isActive, item, onLinkClick, section}) => { return ( - {active && } + {isActive && } {item.title} ); diff --git a/src/utils/isItemActive.js b/src/utils/isItemActive.js index 076b50aa..ba69c090 100644 --- a/src/utils/isItemActive.js +++ b/src/utils/isItemActive.js @@ -20,7 +20,7 @@ const toAnchor = (href = '') => { // This comment should not be true anymore since we're using 300 redirects const isItemActive = (location, item) => { - if (location.hash) { + if (location.hash) { if (item.href) { return location.hash === toAnchor(item.href); } From 15fc32561f9128fa9f7ba7179249d307d2d21c3e Mon Sep 17 00:00:00 2001 From: jxom Date: Sun, 8 Oct 2017 11:34:24 +1100 Subject: [PATCH 4/5] Run check-all script --- src/components/MarkdownPage/MarkdownPage.js | 10 ++-- .../components/Sidebar/ScrollSyncSection.js | 47 +++++++++---------- src/templates/components/Sidebar/Section.js | 14 ++++-- src/templates/components/Sidebar/Sidebar.js | 12 +++-- src/templates/tutorial.js | 2 +- src/utils/createLink.js | 2 +- src/utils/isItemActive.js | 2 +- 7 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/components/MarkdownPage/MarkdownPage.js b/src/components/MarkdownPage/MarkdownPage.js index f4bc96ef..737a84d1 100644 --- a/src/components/MarkdownPage/MarkdownPage.js +++ b/src/components/MarkdownPage/MarkdownPage.js @@ -25,8 +25,8 @@ import createOgUrl from 'utils/createOgUrl'; const MarkdownPage = ({ authors, createLink, - date, - enableScrollSync, + date, + enableScrollSync, ogDescription, location, markdownRemark, @@ -99,7 +99,7 @@ const MarkdownPage = ({
= itemTopOffset.offsetTop; - }); + }); this.setState({ activeItemId: item ? item.id : '', }); @@ -71,12 +71,7 @@ class ScrollSyncSection extends Component { render() { const {activeItemId} = this.state; - return ( -
- ); + return
; } } @@ -103,6 +98,6 @@ const _getElementTopOffsetsById = ids => offsetTop: element.offsetTop, }; }) - .filter(item => item); + .filter(item => item); export default ScrollSyncSection; diff --git a/src/templates/components/Sidebar/Section.js b/src/templates/components/Sidebar/Section.js index 99215c66..4630ccda 100644 --- a/src/templates/components/Sidebar/Section.js +++ b/src/templates/components/Sidebar/Section.js @@ -15,10 +15,10 @@ import MetaTitle from '../MetaTitle'; import ChevronSvg from '../ChevronSvg'; const Section = ({ - activeItemId, + activeItemId, createLink, - isActive, - isScrollSync, + isActive, + isScrollSync, location, onLinkClick, onSectionTitleClick, @@ -66,7 +66,9 @@ const Section = ({ marginTop: 5, }}> {createLink({ - isActive: isScrollSync ? activeItemId === item.id : isItemActive(location, item), + isActive: isScrollSync + ? activeItemId === item.id + : isItemActive(location, item), item, location, onLinkClick, @@ -78,7 +80,9 @@ const Section = ({ {item.subitems.map(subitem => (
  • {createLink({ - isActive: isScrollSync ? activeItemId === subitem.id : isItemActive(location, subitem), + isActive: isScrollSync + ? activeItemId === subitem.id + : isItemActive(location, subitem), item: subitem, location, onLinkClick, diff --git a/src/templates/components/Sidebar/Sidebar.js b/src/templates/components/Sidebar/Sidebar.js index 5765a4c8..770781ff 100644 --- a/src/templates/components/Sidebar/Sidebar.js +++ b/src/templates/components/Sidebar/Sidebar.js @@ -25,10 +25,16 @@ class Sidebar extends Component { } render() { - const {closeParentMenu, createLink, enableScrollSync, location, sectionList} = this.props; - const {activeSection} = this.state; + const { + closeParentMenu, + createLink, + enableScrollSync, + location, + sectionList, + } = this.props; + const {activeSection} = this.state; - const SectionComponent = enableScrollSync ? ScrollSyncSection : Section; + const SectionComponent = enableScrollSync ? ScrollSyncSection : Section; return ( { return ( { ); } return createLinkDocs({ - isActive, + isActive, item, section, }); diff --git a/src/utils/isItemActive.js b/src/utils/isItemActive.js index ba69c090..076b50aa 100644 --- a/src/utils/isItemActive.js +++ b/src/utils/isItemActive.js @@ -20,7 +20,7 @@ const toAnchor = (href = '') => { // This comment should not be true anymore since we're using 300 redirects const isItemActive = (location, item) => { - if (location.hash) { + if (location.hash) { if (item.href) { return location.hash === toAnchor(item.href); } From 39def4b50c34f0259d4fa25bb784d4a4181734c0 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 Oct 2017 10:39:57 -0700 Subject: [PATCH 5/5] Removed no-longer-used isItemActive --- src/utils/createLink.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/createLink.js b/src/utils/createLink.js index c5cedb54..f864725c 100644 --- a/src/utils/createLink.js +++ b/src/utils/createLink.js @@ -14,7 +14,6 @@ import Link from 'gatsby-link'; import React from 'react'; import ExternalLinkSvg from 'templates/components/ExternalLinkSvg'; -import isItemActive from 'utils/isItemActive'; import slugify from 'utils/slugify'; import {colors, media} from 'theme';