Browse Source

Tweak sidebar to update navigation highlight on scroll

main
jxom 8 years ago
parent
commit
94270569a5
  1. 2
      content/tutorial/nav.yml
  2. 85
      src/templates/components/Sidebar/Section.js
  3. 15
      src/utils/createLink.js

2
content/tutorial/nav.yml

@ -1,6 +1,6 @@
- title: Tutorial - title: Tutorial
items: items:
- id: tutorial - id: before-we-start
title: Before We Start title: Before We Start
href: /tutorial/tutorial.html#before-we-start href: /tutorial/tutorial.html#before-we-start
forceInternal: true forceInternal: true

85
src/templates/components/Sidebar/Section.js

@ -9,22 +9,66 @@
'use strict'; 'use strict';
import React from 'react'; import React, {Component} from 'react';
import {colors, media} from 'theme'; import {colors, media} from 'theme';
import MetaTitle from '../MetaTitle'; import MetaTitle from '../MetaTitle';
import ChevronSvg from '../ChevronSvg'; import ChevronSvg from '../ChevronSvg';
// TODO Update isActive link as document scrolls past anchor tags class Section extends Component {
// Maybe used 'hashchange' along with 'scroll' to set/update active links constructor(props, context) {
super(props, context);
const Section = ({ 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, createLink,
isActive, isActive,
location, location,
onLinkClick, onLinkClick,
onSectionTitleClick, onSectionTitleClick,
section, section,
}) => ( } = this.props;
const {activeItemId} = this.state;
return (
<div> <div>
<MetaTitle <MetaTitle
onClick={onSectionTitleClick} onClick={onSectionTitleClick}
@ -71,6 +115,7 @@ const Section = ({
location, location,
onLinkClick, onLinkClick,
section, section,
isActive: activeItemId === item.id,
})} })}
{item.subitems && ( {item.subitems && (
@ -82,6 +127,7 @@ const Section = ({
location, location,
onLinkClick, onLinkClick,
section, section,
isActive: activeItemId === subitem.id,
})} })}
</li> </li>
))} ))}
@ -91,6 +137,33 @@ const Section = ({
))} ))}
</ul> </ul>
</div> </div>
); );
}
}
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; export default Section;

15
src/utils/createLink.js

@ -65,15 +65,22 @@ const createLinkDocs = ({item, location, section}) => {
); );
}; };
const createLinkTutorial = ({item, location, onLinkClick, section}) => { const createLinkTutorial = ({
const isActive = isItemActive(location, item); item,
location,
onLinkClick,
section,
isActive,
}) => {
const active =
typeof isActive === 'boolean' ? isActive : isItemActive(location, item);
return ( return (
<Link <Link
css={[linkCss, isActive && activeLinkCss]} css={[linkCss, active && activeLinkCss]}
onClick={onLinkClick} onClick={onLinkClick}
to={item.href}> to={item.href}>
{isActive && <span css={activeLinkBefore} />} {active && <span css={activeLinkBefore} />}
{item.title} {item.title}
</Link> </Link>
); );

Loading…
Cancel
Save