'use strict'; import React from 'react/addons'; import { History, Lifecycle } from 'react-router'; import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs'; const { arrayOf, element, bool, shape, string, object } = React.PropTypes; const SlidesContainer = React.createClass({ propTypes: { children: arrayOf(element), forwardProcess: bool.isRequired, glyphiconClassNames: shape({ pending: string, complete: string }), location: object, pageExitWarning: string }, mixins: [History, Lifecycle], getInitialState() { return { containerWidth: 0, pageExitWarning: null }; }, componentDidMount() { // we're using an event listener on window here, // as it was not possible to listen to the resize events of a dom node window.addEventListener('resize', this.handleContainerResize); // Initially, we need to dispatch 'resize' once to render correctly window.dispatchEvent(new Event('resize')); }, componentWillUnmount() { window.removeEventListener('resize', this.handleContainerResize); }, routerWillLeave() { return this.props.pageExitWarning; }, handleContainerResize() { this.setState({ // +30 to get rid of the padding of the container which is 15px + 15px left and right containerWidth: this.refs.containerWrapper.getDOMNode().offsetWidth + 30 }); }, // When the start_from parameter is used, this.setSlideNum can not simply be used anymore. nextSlide(additionalQueryParams) { const slideNum = parseInt(this.props.location.query.slide_num, 10) || 0; let nextSlide = slideNum + 1; this.setSlideNum(nextSlide, additionalQueryParams); }, setSlideNum(nextSlideNum, additionalQueryParams = {}) { let queryParams = Object.assign(this.props.location.query, additionalQueryParams); queryParams.slide_num = nextSlideNum; this.history.pushState(null, this.props.location.pathname, queryParams); }, // breadcrumbs are defined as attributes of the slides. // To extract them we have to read the DOM element's attributes extractBreadcrumbs() { const startFrom = parseInt(this.props.location.query.start_from, 10) || -1; let breadcrumbs = []; React.Children.map(this.props.children, (child, i) => { if(child && i >= startFrom && child.props['data-slide-title']) { breadcrumbs.push(child.props['data-slide-title']); } }); return breadcrumbs; }, // If startFrom is defined as a URL parameter, this can manipulate // the number of children that are injected into the DOM. // Therefore React.Children.count does not work anymore and we // need to implement our own method. customChildrenCount() { const startFrom = parseInt(this.props.location.query.start_from, 10) || -1; let count = 0; React.Children.forEach(this.props.children, (child, i) => { if(i >= startFrom) { count++; } }); return count; }, renderBreadcrumbs() { let breadcrumbs = this.extractBreadcrumbs(); let numOfChildren = this.customChildrenCount(); // check if every child/slide has a title, // otherwise do not display the breadcrumbs at all // Also, if there is only one child, do not display the breadcrumbs if(breadcrumbs.length === numOfChildren && breadcrumbs.length > 1 && numOfChildren > 1) { return ( ); } else { return null; } }, // Since we need to give the slides a width, we need to call ReactAddons.addons.cloneWithProps // Also, a key is nice to have! renderChildren() { const startFrom = parseInt(this.props.location.query.start_from, 10) || -1; return React.Children.map(this.props.children, (child, i) => { // since the default parameter of startFrom is -1, we do not need to check // if its actually present in the url bar, as it will just not match if(child && i >= startFrom) { return React.addons.cloneWithProps(child, { className: 'ascribe-slide', style: { width: this.state.containerWidth }, key: i }); } else { // Abortions are bad mkay return null; } }); }, render() { let spacing = this.state.containerWidth * parseInt(this.props.location.query.slide_num, 10) || 0; let translateXValue = 'translateX(' + (-1) * spacing + 'px)'; /* According to the react documentation, all browser vendor prefixes need to be upper cases in the beginning except for the Microsoft one *bigfuckingsurprise* https://facebook.github.io/react/tips/inline-styles.html */ return (
{this.renderBreadcrumbs()}
{this.renderChildren()}
); } }); export default SlidesContainer;