mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 00:21:20 -05:00
Responsive subnav
This commit is contained in:
parent
2e59359d28
commit
30eedda2a2
4 changed files with 263 additions and 124 deletions
|
@ -1,4 +1,5 @@
|
|||
const bindAll = require('lodash.bindall');
|
||||
const classNames = require('classnames');
|
||||
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
// const injectIntl = require('react-intl').injectIntl;
|
||||
|
@ -15,55 +16,78 @@ const Comment = require('../../components/comment/comment.jsx');
|
|||
const Page = require('../../components/page/www/page.jsx');
|
||||
const render = require('../../lib/render.jsx');
|
||||
|
||||
const MediaQuery = require('react-responsive').default;
|
||||
const frameless = require('../../lib/frameless');
|
||||
|
||||
require('./annual-report.scss');
|
||||
|
||||
const SECTIONS = {
|
||||
message: 'message',
|
||||
mission: 'mission',
|
||||
reach: 'reach',
|
||||
milestones: 'milestones',
|
||||
initiatives: 'initiatives',
|
||||
financials: 'financials',
|
||||
supporters: 'supporters',
|
||||
leadership: 'leadership',
|
||||
donate: 'donate'
|
||||
};
|
||||
|
||||
const SECTION_NAMES = {
|
||||
message: <FormattedMessage id="annualReport.subnavMessage" />,
|
||||
mission: <FormattedMessage id="annualReport.subnavMission" />,
|
||||
reach: <FormattedMessage id="annualReport.subnavReach" />,
|
||||
milestones: <FormattedMessage id="annualReport.subnavMilestones" />,
|
||||
initiatives: <FormattedMessage id="annualReport.subnavInitiatives" />,
|
||||
financials: <FormattedMessage id="annualReport.subnavFinancials" />,
|
||||
supporters: <FormattedMessage id="annualReport.subnavSupporters" />,
|
||||
leadership: <FormattedMessage id="annualReport.subnavLeadership" />,
|
||||
donate: <FormattedMessage id="annualReport.subnavDonate" />
|
||||
};
|
||||
|
||||
class AnnualReport extends React.Component {
|
||||
constructor () {
|
||||
super();
|
||||
this.messageRef = null;
|
||||
this.missionRef = null;
|
||||
this.reachRef = null;
|
||||
this.milestonesRef = null;
|
||||
this.initiativesRef = null;
|
||||
this.financialsRef = null;
|
||||
this.supportersRef = null;
|
||||
this.leadershipRef = null;
|
||||
this.donateRef = null;
|
||||
|
||||
this.sectionRefs = {
|
||||
message: () => this.messageRef,
|
||||
mission: () => this.missionRef,
|
||||
reach: () => this.reachRef,
|
||||
milestones: () => this.milestonesRef,
|
||||
initiatives: () => this.initiativesRef,
|
||||
financials: () => this.financialsRef,
|
||||
supporters: () => this.supportersRef,
|
||||
leadership: () => this.leadershipRef,
|
||||
donate: () => this.donateRef
|
||||
// Storage for each of the section refs when we need to refer
|
||||
// to them in the scroll handling code
|
||||
// These will be stored with a short lowercase key representing
|
||||
// the specific section (e.g. 'mission')
|
||||
this.sectionRefs = {};
|
||||
|
||||
this.subnavRef = null;
|
||||
|
||||
this.state = {
|
||||
currentlyVisible: '',
|
||||
dropdownVisible: false
|
||||
};
|
||||
|
||||
bindAll(this, [
|
||||
'scrollTo',
|
||||
'setMessageRef',
|
||||
'setMissionRef',
|
||||
'setReachRef',
|
||||
'setMilestonesRef',
|
||||
'setInitiativesRef',
|
||||
'setFinancialsRef',
|
||||
'setSupportersRef',
|
||||
'setLeadershipRef',
|
||||
'setDonateRef',
|
||||
'handleSubNavItemClick'
|
||||
'setRef',
|
||||
'setSubnavRef',
|
||||
'handleSubnavItemClick',
|
||||
'getDimensionsOfSection',
|
||||
'handleScroll',
|
||||
'handleDropDownClick'
|
||||
]);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
}
|
||||
|
||||
componentWillUnMount () {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
}
|
||||
|
||||
// A generic handler for a subnav item that takes the name of the
|
||||
// section to scroll to (all lowercase)
|
||||
handleSubNavItemClick (sectionName) {
|
||||
handleSubnavItemClick (sectionName) {
|
||||
// Return a button click handler that scrolls to the
|
||||
// correct section
|
||||
return () => {
|
||||
this.scrollTo(this.sectionRefs[sectionName]());
|
||||
this.scrollTo(this.sectionRefs[sectionName]);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -76,94 +100,160 @@ class AnnualReport extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
setMessageRef (ref) {
|
||||
this.messageRef = ref;
|
||||
setRef (sectionName) {
|
||||
return ref => (this.sectionRefs[sectionName] = ref);
|
||||
}
|
||||
setMissionRef (ref) {
|
||||
this.missionRef = ref;
|
||||
|
||||
setSubnavRef (ref) {
|
||||
this.subnavRef = ref;
|
||||
}
|
||||
setReachRef (ref) {
|
||||
this.reachRef = ref;
|
||||
|
||||
getDimensionsOfSection (sectionRef) {
|
||||
const {height} = sectionRef.getBoundingClientRect();
|
||||
const offsetTop = sectionRef.offsetTop;
|
||||
const offsetBottom = offsetTop + height;
|
||||
|
||||
return {
|
||||
height,
|
||||
offsetTop,
|
||||
offsetBottom
|
||||
};
|
||||
}
|
||||
setMilestonesRef (ref) {
|
||||
this.milestonesRef = ref;
|
||||
|
||||
handleScroll () {
|
||||
const subnavHeight = this.getDimensionsOfSection(this.subnavRef).height;
|
||||
// The additional 50 is to account for the main site nav height
|
||||
const currentScrollPosition = window.scrollY + subnavHeight + 50;
|
||||
|
||||
// Find which section is currently visible based on our scroll position
|
||||
for (const key in this.sectionRefs) {
|
||||
if (!this.sectionRefs.hasOwnProperty(key)) continue;
|
||||
const currentRef = this.sectionRefs[key];
|
||||
const {offsetBottom, offsetTop} = this.getDimensionsOfSection(currentRef);
|
||||
if (currentScrollPosition > offsetTop && currentScrollPosition < offsetBottom) {
|
||||
if (this.state.currentlyVisible !== key) {
|
||||
this.setState({currentlyVisible: key});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setInitiativesRef (ref) {
|
||||
this.initiativesRef = ref;
|
||||
}
|
||||
setFinancialsRef (ref) {
|
||||
this.financialsRef = ref;
|
||||
}
|
||||
setSupportersRef (ref) {
|
||||
this.supportersRef = ref;
|
||||
}
|
||||
setLeadershipRef (ref) {
|
||||
this.leadershipRef = ref;
|
||||
}
|
||||
setDonateRef (ref) {
|
||||
this.donateRef = ref;
|
||||
|
||||
handleDropDownClick () {
|
||||
this.setState({dropdownVisible: !this.state.dropdownVisible});
|
||||
}
|
||||
|
||||
render () {
|
||||
const subnav = (<FlexRow className="inner">
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.message}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.message)}
|
||||
>
|
||||
{SECTION_NAMES.message}
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.mission}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.mission)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavMission" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.reach}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.reach)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavReach" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.milestones}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.milestones)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavMilestones" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.initiatives}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.initiatives)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavInitiatives" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.financials}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.financials)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavFinancials" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.supporters}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.supporters)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavSupporters" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.leadership}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.leadership)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavLeadership" />
|
||||
</a>
|
||||
<a
|
||||
className={classNames(
|
||||
{selectedItem: this.state.currentlyVisible === SECTIONS.donate}
|
||||
)}
|
||||
onClick={this.handleSubnavItemClick(SECTIONS.donate)}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavDonate" />
|
||||
</a>
|
||||
</FlexRow>);
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="subnavigation">
|
||||
<FlexRow className="inner">
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('message')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavMessage" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('mission')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavMission" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('reach')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavReach" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('milestones')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavMilestones" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('initiatives')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavInitiatives" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('financials')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavFinancials" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('supporters')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavSupporters" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('leadership')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavLeadership" />
|
||||
</a>
|
||||
<a
|
||||
className="link"
|
||||
onClick={this.handleSubNavItemClick('donate')}
|
||||
>
|
||||
<FormattedMessage id="annualReport.subnavDonate" />
|
||||
</a>
|
||||
</FlexRow>
|
||||
<div
|
||||
className="subnavigation"
|
||||
ref={this.setSubnavRef}
|
||||
>
|
||||
{/* Top Bar */}
|
||||
<MediaQuery maxWidth={frameless.tabletPortrait - 1}>
|
||||
<div className="sectionIndicator inner" >
|
||||
<p>
|
||||
{SECTION_NAMES[this.state.currentlyVisible]}
|
||||
</p>
|
||||
<Button
|
||||
className="dropdown-button"
|
||||
onClick={this.handleDropDownClick}
|
||||
>
|
||||
<img
|
||||
className={classNames({rotated: this.state.subNavVisible})}
|
||||
src="/images/annual-report/dropdown-arrow.svg"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
{this.state.dropdownVisible ?
|
||||
<div>
|
||||
<hr />
|
||||
{subnav}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</MediaQuery>
|
||||
{/* Bottom Bar */}
|
||||
<MediaQuery minWidth={frameless.tabletPortrait}>
|
||||
{subnav}
|
||||
</MediaQuery>
|
||||
</div>
|
||||
<div className="banner-wrapper">
|
||||
<TitleBanner className="masthead masthead">
|
||||
|
@ -184,7 +274,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="mission section"
|
||||
id="mission"
|
||||
ref={this.setMissionRef}
|
||||
ref={this.setRef(SECTIONS.mission)}
|
||||
>
|
||||
<div className="inner">
|
||||
</div>
|
||||
|
@ -192,7 +282,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="milestones-section section"
|
||||
id="milestones"
|
||||
ref={this.setMilestonesRef}
|
||||
ref={this.setRef(SECTIONS.milestones)}
|
||||
>
|
||||
<div className="inner">
|
||||
<div className="milestones-intro">
|
||||
|
@ -239,7 +329,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="our-reach section"
|
||||
id="reach"
|
||||
ref={this.setReachRef}
|
||||
ref={this.setRef(SECTIONS.reach)}
|
||||
>
|
||||
<div className="inner">
|
||||
<section className="ttt-section">
|
||||
|
@ -265,7 +355,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="initiatives section"
|
||||
id="initiatives"
|
||||
ref={this.setInitiativesRef}
|
||||
ref={this.setRef(SECTIONS.initiatives)}
|
||||
>
|
||||
<div className="inner">
|
||||
<section className="ttt-section">
|
||||
|
@ -291,7 +381,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="financials-section section"
|
||||
id="financials"
|
||||
ref={this.setFinancialsRef}
|
||||
ref={this.setRef(SECTIONS.financials)}
|
||||
>
|
||||
<div className="inner">
|
||||
<h2 className="financials-h2">
|
||||
|
@ -429,7 +519,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="supporters-section section"
|
||||
id="supporters"
|
||||
ref={this.setSupportersRef}
|
||||
ref={this.setRef(SECTIONS.supporters)}
|
||||
>
|
||||
<div className="inner">
|
||||
<div className="supporters-heading">
|
||||
|
@ -615,7 +705,7 @@ class AnnualReport extends React.Component {
|
|||
<div
|
||||
className="donate-section section"
|
||||
id="donate"
|
||||
ref={this.setDonateRef}
|
||||
ref={this.setRef(SECTIONS.donate)}
|
||||
>
|
||||
<FlexRow className="donate-info">
|
||||
<img src="/images/annual-report/donate-illustration.svg" />
|
||||
|
|
|
@ -39,7 +39,8 @@ h5 {
|
|||
}
|
||||
|
||||
.section {
|
||||
scroll-margin-top: 108px; // This is the height of the sub nav + height of the site nav
|
||||
// This is the height of the sub nav + height of the site nav - a few pixels...
|
||||
scroll-margin-top: 104px;
|
||||
}
|
||||
|
||||
.banner-wrapper {
|
||||
|
@ -715,32 +716,63 @@ img.tips-icon {
|
|||
}
|
||||
|
||||
.subnavigation {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 50px;
|
||||
z-index: 9;
|
||||
box-shadow: 0 0 3px $box-shadow-gray;
|
||||
background-color: #4280D7;
|
||||
padding: 0;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 58px;
|
||||
|
||||
.flex-row {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
color: $type-white;
|
||||
font-size: .85rem;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
&.selectedItem, &:hover {
|
||||
border-bottom: 1px solid $ui-white;
|
||||
}
|
||||
}
|
||||
|
||||
.sectionIndicator {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
height: 50px;
|
||||
|
||||
p {
|
||||
color: $ui-white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
background: none;
|
||||
border: 0px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
.rotated {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media #{$medium-and-smaller} {
|
||||
height: auto;
|
||||
|
||||
> .flex-row {
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
-webkit-flex-direction: row;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"annualReport.subnavMission": "Mission",
|
||||
"annualReport.subnavMilestones": "Milestones",
|
||||
"annualReport.subnavReach": "Reach",
|
||||
"annualReport.subnavWork": "Work",
|
||||
"annualReport.subnavInitiatives": "Initiatives",
|
||||
"annualReport.subnavFinancials": "Financials",
|
||||
"annualReport.subnavSupporters": "Supporters",
|
||||
"annualReport.subnavLeadership": "Leadership",
|
||||
|
|
17
static/images/annual-report/dropdown-arrow.svg
Normal file
17
static/images/annual-report/dropdown-arrow.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 63.1 (92452) - https://sketch.com -->
|
||||
<title>Caret Icon</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="R1.6_Columns-4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="R1.6---4-Column" transform="translate(-306.000000, -62.000000)" fill="#FFFFFF">
|
||||
<g id="V3---SubNav" transform="translate(0.000000, 52.000000)">
|
||||
<g id="Closed">
|
||||
<g id="Caret-Icon" transform="translate(306.000000, 10.000000)">
|
||||
<path d="M12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 Z M15.6853864,10.3100473 C15.2671176,9.89665089 8.73288244,9.89665089 8.31461357,10.3100473 C7.89512881,10.724642 7.89512881,11.3932658 8.31461357,11.8066623 L8.31461357,11.8066623 L11.2412797,14.6896531 C11.4516301,14.8969505 11.7252071,15 12,15 C12.2760088,15 12.5495858,14.8969505 12.7599362,14.6896531 L12.7599362,14.6896531 L15.6853864,11.8066623 C16.1048712,11.3932658 16.1048712,10.7234438 15.6853864,10.3100473 Z" id="Combined-Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
Loading…
Reference in a new issue