const bindAll = require('lodash.bindall');
const classNames = require('classnames');
const React = require('react');
const MediaQuery = require('react-responsive').default;
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const render = require('../../lib/render.jsx');
const frameless = require('../../lib/frameless');
const Page = require('../../components/page/www/page.jsx');
const Button = require('../../components/forms/button.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Comment = require('../../components/comment/comment.jsx');
const WorldMap = require('../../components/world-map/world-map.jsx');
const CountryUsage = require('./country-usage.json');
const PeopleGrid = require('../../components/people-grid/people-grid.jsx');
const People = require('./people.json');
require('./annual-report.scss');
// Some constants used for the page subnav and section refs
const SECTIONS = {
message: 'message',
mission: 'mission',
reach: 'reach',
milestones: 'milestones',
initiatives: 'initiatives',
financials: 'financials',
supporters: 'supporters',
leadership: 'leadership',
donate: 'donate'
};
const SECTION_NAMES = {
message: ,
mission: ,
reach: ,
milestones: ,
initiatives: ,
financials: ,
supporters: ,
leadership: ,
donate:
};
// Constants used for world map data processing/formatting for use with Plotly
const countryKeys = Object.keys(CountryUsage);
const countryNames = countryKeys.map(key => CountryUsage[key].display);
const countryData = countryKeys.map(key => CountryUsage[key].count);
const colorIndex = countryKeys.map(key => CountryUsage[key]['log count']);
class AnnualReport extends React.Component {
constructor (props) {
super(props);
// 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: SECTIONS.message, // The currently visible section
dropdownVisible: false
};
bindAll(this, [
'scrollTo',
'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) {
// Return a button click handler that will close the dropdown if open
// and scrolls to the correct section
return () => {
this.setState({dropdownVisible: false});
this.scrollTo(this.sectionRefs[sectionName]);
};
}
scrollTo (element) {
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
// Generically create a ref for the given section, stored in
// this.sectionRefs
setRef (sectionName) {
return ref => (this.sectionRefs[sectionName] = ref);
}
setSubnavRef (ref) {
this.subnavRef = ref;
}
// Calculate the dimensions of a given section for use in figuring out
// which section is currently visible
getDimensionsOfSection (sectionRef) {
const {height} = sectionRef.getBoundingClientRect();
const offsetTop = sectionRef.offsetTop;
const offsetBottom = offsetTop + height;
return {
height,
offsetTop,
offsetBottom
};
}
// While scrolling, update the subnav to reflect the currently visible section
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;
}
}
}
}
// Click handler for responsive subnav dropdown
handleDropDownClick () {
this.setState({dropdownVisible: !this.state.dropdownVisible});
}
render () {
// Element containing buttons to scroll to each of the sections in the
// annual report. The layout of this component will be different on
// different screen sizes (see below)
const subnav =
(
{SECTION_NAMES.message}
);
return (
{/* Top Bar */}
{SECTION_NAMES[this.state.currentlyVisible]}
{this.state.dropdownVisible ?
/* Bottom Bar */
{subnav}
:
null
}
{/* Bottom Bar */}
{subnav}
{/* Show the wave icon inside this div in smaller screens */}