import React from 'react';
import { findDOMNode } from 'react-dom';
import TranslatedComponent from './TranslatedComponent';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
/**
* Available modules menu
*/
export default class AvailableModulesMenu extends TranslatedComponent {
static propTypes = {
modules: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.array]).isRequired,
onSelect: React.PropTypes.func.isRequired,
diffDetails: React.PropTypes.func,
m: React.PropTypes.object,
shipMass: React.PropTypes.number,
warning: React.PropTypes.func
};
static defaultProps = {
shipMass: 0
};
/**
* Constructor
* @param {Object} props React Component properties
* @param {Object} context React Component context
*/
constructor(props, context) {
super(props);
this._hideDiff = this._hideDiff.bind(this);
this.state = this._initState(props, context);
}
/**
* Initiate the list of available moduels
* @param {Object} props React Component properties
* @param {Object} context React Component context
* @return {Object} list: Array of React Components, currentGroup Component if any
*/
_initState(props, context) {
let translate = context.language.translate;
let { m, warning, shipMass, onSelect, modules } = props;
let list, currentGroup;
let buildGroup = this._buildGroup.bind(
this,
translate,
m,
warning,
shipMass - (m && m.mass ? m.mass : 0),
(m, event) => {
this._hideDiff(event);
onSelect(m);
}
);
if (modules instanceof Array) {
list = buildGroup(modules[0].grp, modules);
} else {
list = [];
// At present time slots with grouped options (Hardpoints and Internal) can be empty
list.push(
{translate('empty')}
);
for (let g in modules) {
if (m && g == m.grp) {
list.push( this.groupElem = elem} key={g} className={'select-group cap'}>{translate(g)}
);
} else {
list.push({translate(g)}
);
}
list.push(buildGroup(g, modules[g]));
}
}
return { list, currentGroup };
}
/**
* Generate React Components for Module Group
* @param {Function} translate Translate function
* @param {Objecy} mountedModule Mounted Module
* @param {Funciton} warningFunc Warning function
* @param {number} mass Mass
* @param {function} onSelect Select/Mount callback
* @param {string} grp Group name
* @param {Array} modules Available modules
* @return {React.Component} Available Module Group contents
*/
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) {
let prevClass = null, prevRating = null;
let elems = [];
const sortedModules = modules.sort(this._moduleOrder);
for (let i = 0; i < sortedModules.length; i++) {
let m = sortedModules[i];
let mount = null;
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
let active = mountedModule && mountedModule.id === m.id;
let classes = cn(m.name ? 'lc' : 'c', {
warning: !disabled && warningFunc && warningFunc(m),
active,
disabled
});
let eventHandlers;
if (disabled || active) {
eventHandlers = {};
} else {
let showDiff = this._showDiff.bind(this, mountedModule, m);
let select = onSelect.bind(null, m);
eventHandlers = {
onMouseEnter: this._over.bind(this, showDiff),
onTouchStart: this._touchStart.bind(this, showDiff),
onTouchEnd: this._touchEnd.bind(this, select),
onMouseLeave: this._hideDiff,
onClick: select
};
}
switch(m.mount) {
case 'F': mount = ; break;
case 'G': mount = ; break;
case 'T': mount = ; break;
}
if (i > 0 && sortedModules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
elems.push(
);
}
elems.push(
{mount}
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
);
prevClass = m.class;
prevRating = m.rating;
}
return ;
}
/**
* Generate tooltip content for the difference between the
* mounted module and the hovered modules
* @param {Object} mm The module mounet currently
* @param {Object} m The hovered module
* @param {DOMRect} rect DOMRect for target element
*/
_showDiff(mm, m, rect) {
if (this.props.diffDetails) {
this.touchTimeout = null;
this.context.tooltip(this.props.diffDetails(m, mm), rect);
}
}
/**
* Mouse over diff handler
* @param {Function} showDiff diff tooltip callback
* @param {SyntheticEvent} event Event
*/
_over(showDiff, event) {
event.preventDefault();
showDiff(event.currentTarget.getBoundingClientRect());
}
/**
* Toucch Start - Show diff after press, otherwise treat as tap
* @param {Function} showDiff diff tooltip callback
* @param {SyntheticEvent} event Event
*/
_touchStart(showDiff, event) {
event.preventDefault();
let rect = event.currentTarget.getBoundingClientRect();
this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD);
}
/**
* Touch End - Select module on tap
* @param {Function} select Select module callback
* @param {SyntheticEvent} event Event
*/
_touchEnd(select, event) {
event.preventDefault();
if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet
select();
}
this._hideDiff();
}
/**
* Hide diff tooltip
* @param {SyntheticEvent} event Event
*/
_hideDiff(event) {
clearTimeout(this.touchTimeout);
this.touchTimeout = null;
this.context.tooltip();
}
/**
* Order two modules suitably for display in module selection
* @param {Object} a the first module
* @param {Object} b the second module
* @return {int} -1 if the first module should go first, 1 if the second module should go first
*/
_moduleOrder(a, b) {
// Named modules go last
if (!a.name && b.name) {
return -1;
}
if (a.name && !b.name) {
return 1;
}
// Class ordered from highest (8) to lowest (1)
if (a.class < b.class) {
return 1;
}
if (a.class > b.class) {
return -1;
}
// Mount type, if applicable
if (a.mount && b.mount && a.mount !== b.mount) {
if (a.mount === 'F' || (a.mount === 'G' && b.mount === 'T')) {
return -1;
} else {
return 1;
}
}
// Rating ordered from lowest (E) to highest (A)
if (a.rating < b.rating) {
return 1;
}
if (a.rating > b.rating) {
return -1;
}
// Do not attempt to order by name at this point, as that mucks up the order of armour
return 0;
}
/**
* Scroll to mounted (if it exists) module group on mount
*/
componentDidMount() {
if (this.groupElem) { // Scroll to currently selected group
findDOMNode(this).scrollTop = this.groupElem.offsetTop;
}
}
/**
* Update state based on property and context changes
* @param {Object} nextProps Incoming/Next properties
* @param {Object} nextContext Incoming/Next conext
*/
componentWillReceiveProps(nextProps, nextContext) {
this.setState(this._initState(nextProps, nextContext));
}
/**
* Render the list
* @return {React.Component} List
*/
render() {
return (
e.stopPropagation() }
onContextMenu={stopCtxPropagation}
>
{this.state.list}
);
}
}