1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

Refactor SlidesContainer to only use queryParams to keep slidepage state

This commit is contained in:
Tim Daubenschütz 2015-10-14 14:22:53 +02:00
parent d6b7060f39
commit dab7503601

View File

@ -1,94 +1,42 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react/addons';
import { History } from 'react-router'; import { History } from 'react-router';
import ReactAddons from 'react/addons';
import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs'; import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs';
let SlidesContainer = React.createClass({ const { arrayOf, element, bool, shape, string, object } = React.PropTypes;
propTypes: {
children: React.PropTypes.arrayOf(React.PropTypes.element),
forwardProcess: React.PropTypes.bool.isRequired,
glyphiconClassNames: React.PropTypes.shape({ const SlidesContainer = React.createClass({
pending: React.PropTypes.string, propTypes: {
complete: React.PropTypes.string children: arrayOf(element),
forwardProcess: bool.isRequired,
glyphiconClassNames: shape({
pending: string,
complete: string
}), }),
location: React.PropTypes.object location: object
}, },
mixins: [History], mixins: [History],
getInitialState() { getInitialState() {
// handle queryParameters
let queryParams = this.props.location.query;
let slideNum = -1;
let startFrom = -1;
// We can actually need to check if slide_num is present as a key in queryParams.
// We do not really care about its value though...
if(queryParams && 'slide_num' in queryParams) {
slideNum = parseInt(queryParams.slide_num, 10);
}
// if slide_num is not set, this will be done in componentDidMount
// the query param 'start_from' removes all slide children before the respective number
// Also, we use the 'in' keyword for the same reason as above in 'slide_num'
if(queryParams && 'start_from' in queryParams) {
startFrom = parseInt(queryParams.start_from, 10);
}
return { return {
slideNum, containerWidth: 0
startFrom,
containerWidth: 0,
historyLength: window.history.length
}; };
}, },
componentDidMount() { componentDidMount() {
// check if slide_num was defined, and if not then default to 0
let queryParams = this.props.location.query;
// We use 'in' to check if the key is present in the user's browser url bar,
// we do not really care about its value at this point
if(!('slide_num' in queryParams)) {
// we're first requiring all the other possible queryParams and then set
// the specific one we need instead of overwriting them
queryParams.slide_num = 0;
this.history.replaceState(null, this.props.location.pathname, queryParams);
}
// init container width
this.handleContainerResize(); this.handleContainerResize();
// we're using an event listener on window here, // we're using an event listener on window here,
// as it was not possible to listen to the resize events of a dom node // as it was not possible to listen to the resize events of a dom node
window.addEventListener('resize', this.handleContainerResize); window.addEventListener('resize', this.handleContainerResize);
},
componentWillReceiveProps() { // Initially, we need to dispatch 'resize' once to render correctly
let queryParams = this.props.location.query; window.dispatchEvent(new Event('resize'));
// also check if start_from was updated
// This applies for example when the user tries to submit a already existing piece
// (starting from slide 1 for example) and then clicking on + NEW WORK
if(queryParams && !('start_from' in queryParams)) {
this.setState({
startFrom: -1
});
}
},
componentDidUpdate() {
let queryParams = this.props.location.query;
// check if slide_num was defined, and if not then default to 0
this.setSlideNum(queryParams.slide_num);
}, },
componentWillUnmount() { componentWillUnmount() {
@ -104,79 +52,26 @@ let SlidesContainer = React.createClass({
// When the start_from parameter is used, this.setSlideNum can not simply be used anymore. // When the start_from parameter is used, this.setSlideNum can not simply be used anymore.
nextSlide() { nextSlide() {
let nextSlide = this.state.slideNum + 1; const slideNum = parseInt(this.props.location.query.slide_num, 10);
let nextSlide = slideNum + 1;
this.setSlideNum(nextSlide); this.setSlideNum(nextSlide);
}, },
// We let every one from the outsite set the page number of the slider, setSlideNum(nextSlideNum) {
// though only if the slideNum is actually in the range of our children-list.
setSlideNum(slideNum) {
// we do not want to overwrite other queryParams
let queryParams = this.props.location.query; let queryParams = this.props.location.query;
queryParams.slide_num = nextSlideNum;
// slideNum can in some instances be not a number, this.history.pushState(null, this.props.location.pathname, queryParams);
// therefore we have to parse it to one and make sure that its not NaN
slideNum = parseInt(slideNum, 10);
// if slideNum is not a number (even after we parsed it to one) and there has
// never been a transition to another slide (this.state.slideNum ==== -1 indicates that)
// then we want to "replace" (in this case append) the current url with ?slide_num=0
if(isNaN(slideNum) && this.state.slideNum === -1) {
slideNum = 0;
queryParams.slide_num = slideNum;
this.history.replaceState(null, this.props.location.pathname, queryParams);
this.setState({slideNum: slideNum});
return;
// slideNum always represents the future state. So if slideNum and
// this.state.slideNum are equal, there is no sense in redirecting
} else if(slideNum === this.state.slideNum) {
return;
// if slideNum is within the range of slides and none of the previous cases
// where matched, we can actually do transitions
} else if(slideNum >= 0 || slideNum < this.customChildrenCount()) {
if(slideNum !== this.state.slideNum - 1) {
// Bootstrapping the component, getInitialState is called once to save
// the tabs history length.
// In order to know if we already pushed a new state on the history stack or not,
// we're comparing the old history length with the new one and if it didn't change then
// we push a new state on it ONCE (ever).
// Otherwise, we're able to use the browsers history.forward() method
// to keep the stack clean
if(this.props.forwardProcess) {
queryParams.slide_num = slideNum;
this.history.pushState(null, this.props.location.pathname, queryParams);
} else {
if(this.state.historyLength === window.history.length) {
queryParams.slide_num = slideNum;
this.history.pushState(null, this.props.location.pathname, queryParams);
} else {
window.history.forward();
}
}
}
this.setState({
slideNum: slideNum
});
} else {
throw new Error('You\'re calling a page number that is out of range: ' + slideNum);
}
}, },
// breadcrumbs are defined as attributes of the slides. // breadcrumbs are defined as attributes of the slides.
// To extract them we have to read the DOM element's attributes // To extract them we have to read the DOM element's attributes
extractBreadcrumbs() { extractBreadcrumbs() {
const startFrom = parseInt(this.props.location.query.start_from, 10) || -1;
let breadcrumbs = []; let breadcrumbs = [];
ReactAddons.Children.map(this.props.children, (child, i) => { React.Children.map(this.props.children, (child, i) => {
if(child && i >= this.state.startFrom && child.props['data-slide-title']) { if(child && i >= startFrom && child.props['data-slide-title']) {
breadcrumbs.push(child.props['data-slide-title']); breadcrumbs.push(child.props['data-slide-title']);
} }
}); });
@ -189,9 +84,11 @@ let SlidesContainer = React.createClass({
// Therefore React.Children.count does not work anymore and we // Therefore React.Children.count does not work anymore and we
// need to implement our own method. // need to implement our own method.
customChildrenCount() { customChildrenCount() {
const startFrom = parseInt(this.props.location.query.start_from, 10) || -1;
let count = 0; let count = 0;
React.Children.forEach(this.props.children, (child, i) => { React.Children.forEach(this.props.children, (child, i) => {
if(i >= this.state.startFrom) { if(i >= startFrom) {
count++; count++;
} }
}); });
@ -210,7 +107,7 @@ let SlidesContainer = React.createClass({
return ( return (
<SlidesContainerBreadcrumbs <SlidesContainerBreadcrumbs
breadcrumbs={breadcrumbs} breadcrumbs={breadcrumbs}
slideNum={this.state.slideNum} slideNum={parseInt(this.props.location.query.slide_num, 10)}
numOfSlides={breadcrumbs.length} numOfSlides={breadcrumbs.length}
containerWidth={this.state.containerWidth} containerWidth={this.state.containerWidth}
glyphiconClassNames={this.props.glyphiconClassNames}/> glyphiconClassNames={this.props.glyphiconClassNames}/>
@ -223,12 +120,13 @@ let SlidesContainer = React.createClass({
// Since we need to give the slides a width, we need to call ReactAddons.addons.cloneWithProps // Since we need to give the slides a width, we need to call ReactAddons.addons.cloneWithProps
// Also, a key is nice to have! // Also, a key is nice to have!
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => { 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 // 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 its actually present in the url bar, as it will just not match
if(child && i >= this.state.startFrom) { if(child && i >= startFrom) {
return ReactAddons.addons.cloneWithProps(child, { return React.addons.cloneWithProps(child, {
className: 'ascribe-slide', className: 'ascribe-slide',
style: { style: {
width: this.state.containerWidth width: this.state.containerWidth
@ -244,7 +142,7 @@ let SlidesContainer = React.createClass({
}, },
render() { render() {
let spacing = this.state.containerWidth * this.state.slideNum; let spacing = this.state.containerWidth * parseInt(this.props.location.query.slide_num, 10);
let translateXValue = 'translateX(' + (-1) * spacing + 'px)'; let translateXValue = 'translateX(' + (-1) * spacing + 'px)';
/* /*
@ -278,4 +176,4 @@ let SlidesContainer = React.createClass({
} }
}); });
export default SlidesContainer; export default SlidesContainer;