2017-06-08_509bba0/509bba0_unpacked_with_node_.../discord_app/components/TutorialIndicators.js

198 lines
6.0 KiB
JavaScript
Executable File

import React from 'react';
import ReactDOM from 'react-dom';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Flux from '../lib/flux/index';
import TutorialStore from '../stores/TutorialStore';
import WindowStore from '../stores/WindowStore';
import PopoutStore from '../stores/PopoutStore';
import '../styles/tutorial.styl';
import lodash from 'lodash';
import TutorialActionCreators from '../actions/TutorialActionCreators';
import TutorialData from '../data/tutorial_tips.json';
import Popout from './common/Popout';
import TutorialPopout from './TutorialPopout';
import classNames from 'classnames';
const TutorialIndicator = React.createClass({
mixins: [PureRenderMixin, Flux.StoreListenerMixin(PopoutStore)],
propTypes: {
position: React.PropTypes.oneOf(['left', 'top', 'right', 'bottom']).isRequired,
x: React.PropTypes.number.isRequired,
y: React.PropTypes.number.isRequired,
targetWidth: React.PropTypes.number.isRequired,
targetHeight: React.PropTypes.number.isRequired,
tutorialId: React.PropTypes.string.isRequired,
offset: React.PropTypes.shape({x: React.PropTypes.number, y: React.PropTypes.number}).isRequired,
offsetPercent: React.PropTypes.shape({x: React.PropTypes.number, y: React.PropTypes.number}).isRequired,
},
getInitialState() {
return {
offsetX: null,
offsetY: null,
definition: TutorialData[this.props.tutorialId],
};
},
getStateFromStores() {
return {
isPopoutOpen: PopoutStore.isOpen(this.props.tutorialId),
};
},
componentDidMount() {
this.updateCache();
},
componentDidUpdate({tutorialId, x, y, targetWidth, targetHeight, position, offset, offsetPercent}) {
if (
this.props.tutorialId != tutorialId ||
this.props.x !== x ||
this.props.y !== y ||
this.props.targetWidth !== targetWidth ||
this.props.targetHeight !== targetHeight ||
this.props.position !== position ||
this.props.offset.x !== offset.x ||
this.props.offset.y !== offset.y ||
this.props.offsetPercent.x !== offsetPercent.x ||
this.props.offsetPercent.y !== offsetPercent.y
) {
this.updateCache();
}
},
updateCache() {
const newState = {
offsetX: (this.props.targetWidth - ReactDOM.findDOMNode(this).offsetWidth) / 2,
offsetY: (this.props.targetHeight - ReactDOM.findDOMNode(this).offsetHeight) / 2,
};
const position = this.props.position;
switch (position) {
case 'left':
newState.offsetX = 0;
newState.offsetY = this.props.targetHeight / 2;
break;
case 'right':
newState.offsetX = this.props.targetWidth;
newState.offsetY = this.props.targetHeight / 2;
break;
case 'bottom':
newState.offsetX = this.props.targetWidth / 2;
newState.offsetY = this.props.targetHeight;
break;
case 'top':
default:
newState.offsetX = this.props.targetWidth / 2;
newState.offsetY = 0;
}
const sanititzedOffset = {
x: this.props.offset.x ? this.props.offset.x : 0,
y: this.props.offset.y ? this.props.offset.y : 0,
};
const sanititzedOffsetPercent = {
x: this.props.offsetPercent.x ? this.props.offsetPercent.x : 0,
y: this.props.offsetPercent.y ? this.props.offsetPercent.y : 0,
};
newState.offsetX += sanititzedOffset.x + sanititzedOffsetPercent.x / 100 * this.props.targetWidth;
newState.offsetY += sanititzedOffset.y + sanititzedOffsetPercent.y / 100 * this.props.targetHeight;
this.setState(newState);
},
handleSkipTips() {
TutorialActionCreators.suppressAll();
},
handlePopoutClose(wasConfirmed) {
if (wasConfirmed) {
TutorialActionCreators.dismiss(this.props.tutorialId);
}
},
renderTutorialPopout(props) {
return (
<TutorialPopout
{...props}
tutorialId={this.props.tutorialId}
onSkipAll={this.handleSkipTips}
onClose={this.handlePopoutClose}
/>
);
},
render() {
const style = {
left: this.state.offsetX === null ? null : this.props.x + this.state.offsetX,
top: this.state.offsetY === null ? null : this.props.y + this.state.offsetY,
};
const shouldRender = this.props.isFocused && !this.state.isPopoutOpen;
const exclamationMark = this.state.definition.highPriority !== true
? null
: <div>
<div className={classNames('top', {animating: shouldRender, 'not-animating': !shouldRender})} />
<div className={classNames('bottom', {animating: shouldRender, 'not-animating': !shouldRender})} />
</div>;
const styleClasses = {
animating: shouldRender,
'not-animating': !shouldRender,
highPriority: this.state.definition.highPriority,
};
return (
<Popout
render={this.renderTutorialPopout}
position={this.state.definition.popoutPosition}
backdrop={true}
uniqueId={this.props.tutorialId}>
<div className="indicator" style={style}>
<div className={classNames('animation-container', styleClasses)}>
{exclamationMark}
<div className={classNames('inner-circle', styleClasses)} />
<div className={classNames('outer-circle', styleClasses)} />
</div>
</div>
</Popout>
);
},
});
const TutorialIndicators = React.createClass({
mixins: [Flux.StoreListenerMixin(TutorialStore, WindowStore)],
getStateFromStores() {
return {
indicators: TutorialStore.getIndicators(),
shouldShowAny: TutorialStore.shouldShowAnyIndicators(),
isFocused: WindowStore.isFocused(),
};
},
render() {
let indicators = null;
if (this.state.shouldShowAny) {
indicators = lodash.map(this.state.indicators, (props, tutorialId) => {
return (
<TutorialIndicator key={tutorialId} tutorialId={tutorialId} isFocused={this.state.isFocused} {...props} />
);
});
}
return <div className="tutorial-indicators">{indicators}</div>;
},
});
export default TutorialIndicators;
// WEBPACK FOOTER //
// ./discord_app/components/TutorialIndicators.js