|
@ -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; |
|
|