Continued porting to react

This commit is contained in:
Colin McLeod
2016-02-02 09:13:59 -08:00
parent b42a812a45
commit d783a38588
43 changed files with 679 additions and 411 deletions

View File

@@ -22,12 +22,13 @@ export default class ActiveLink extends Link {
* @return {React.Component} The active link
*/
render() {
let action = this.handler.bind(this);
let className = this.props.className;
if (isActive(this.props.href)) {
className = cn(className, 'active');
}
return <a {...this.props} className={className} onClick={this.handler.bind(this)}>{this.props.children}</a>;
return <a {...this.props} className={className} onTouchTap={action}>{this.props.children}</a>;
}
}

View File

@@ -1,6 +1,7 @@
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';
@@ -30,6 +31,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
constructor(props, context) {
super(props);
this._hideDiff = this._hideDiff.bind(this);
this._diffMove = this._diffMove.bind(this);
this.state = { list: this._initList(props, context) };
}
@@ -49,8 +51,8 @@ export default class AvailableModulesMenu extends TranslatedComponent {
m,
warning,
shipMass - (m && m.mass ? m.mass : 0),
(m) => {
this._hideDiff();
(m, event) => {
this._hideDiff(event);
onSelect(m);
}
);
@@ -89,9 +91,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
let m = modules[i];
let mount = null;
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
let active = mountedModule && mountedModule === m;
let classes = cn(m.name ? 'lc' : 'c', {
active: mountedModule && mountedModule.id === m.id,
warning: !disabled && warningFunc && warningFunc(m),
active,
disabled
});
@@ -105,11 +108,14 @@ export default class AvailableModulesMenu extends TranslatedComponent {
elems.push(<br key={m.grp + i} />);
}
let showDiff = disabled || active ? null : this._showDiff.bind(this, mountedModule, m);
elems.push(
<li
key={m.id}
className={classes}
onMouseOver={disabled ? null : this._showDiff.bind(this, mountedModule, m)}
onMouseEnter={showDiff}
onTouchStart={showDiff}
onMouseLeave={this._hideDiff}
onClick={disabled ? null : onSelect.bind(null, m)}
>
@@ -132,15 +138,27 @@ export default class AvailableModulesMenu extends TranslatedComponent {
* @param {SyntheticEvent} event Event
*/
_showDiff(mm, m, event) {
event.preventDefault();
if (this.props.diffDetails) {
this.context.tooltip(this.props.diffDetails(m, mm), event.currentTarget.getBoundingClientRect());
}
}
_touchStart(event) {
event.preventDefault();
console.log(Object.assign({}, event));
}
_diffMove(event) {
console.log(Object.assign({}, event));
}
/**
* Hide diff tooltip
* @param {SyntheticEvent} event Event
*/
_hideDiff() {
_hideDiff(event) {
event.preventDefault();
this.context.tooltip();
}
@@ -170,7 +188,15 @@ export default class AvailableModulesMenu extends TranslatedComponent {
*/
render() {
return (
<div className={cn('select', this.props.className)} onScroll={this._hideDiff} onClick={(e) => e.stopPropagation() }>
<div
className={cn('select', this.props.className)}
onScroll={this._hideDiff}
onClick={(e) => e.stopPropagation() }
onTouchStart={this._touchStart}
onTouchEnd={this._hideDiff}
onTouchCancel={this._hideDiff}
onContextMenu={stopCtxPropagation}
>
{this.state.list}
</div>
);

View File

@@ -1,5 +1,4 @@
import React from 'react';
import { findDOMNode } from 'react-dom';
import d3 from 'd3';
import TranslatedComponent from './TranslatedComponent';

View File

@@ -38,8 +38,8 @@ export default class ComparisonTable extends TranslatedComponent {
*/
_buildHeaders(facets, onSort, translate) {
let header = [
<th key='ship' rowSpan='2' className='sortable' onClick={onSort.bind(null, 'name')}>{translate('ship')}</th>,
<th key='build' rowSpan='2' className='sortable' onClick={onSort.bind(null, 'buildName')}>{translate('build')}</th>
<th key='ship' rowSpan='2' className='sortable' onTouchTap={onSort.bind(null, 'name')}>{translate('ship')}</th>,
<th key='build' rowSpan='2' className='sortable' onTouchTap={onSort.bind(null, 'buildName')}>{translate('build')}</th>
];
let subHeader = [];
@@ -47,13 +47,13 @@ export default class ComparisonTable extends TranslatedComponent {
if (f.active) {
let p = f.props;
let pl = p.length;
header.push(<th key={f.title} rowSpan={pl === 1 ? 2 : 1} colSpan={pl} className={cn({ sortable: pl === 1 })} onClick={pl === 1 ? onSort.bind(null, p[0]) : null }>
header.push(<th key={f.title} rowSpan={pl === 1 ? 2 : 1} colSpan={pl} className={cn({ sortable: pl === 1 })} onTouchTap={pl === 1 ? onSort.bind(null, p[0]) : null }>
{translate(f.title)}
</th>);
if (pl > 1) {
for (let i = 0; i < pl; i++) {
subHeader.push(<th key={p[i]} className={cn('sortable', { lft: i === 0 })} onClick={onSort.bind(null, p[i])} >{translate(f.lbls[i])}</th>);
subHeader.push(<th key={p[i]} className={cn('sortable', { lft: i === 0 })} onTouchTap={onSort.bind(null, p[i])} >{translate(f.lbls[i])}</th>);
}
}
}

View File

@@ -295,9 +295,9 @@ export default class CostSection extends TranslatedComponent {
if (item.m && item.m.cost) {
let toggle = this._toggleCost.bind(this, item);
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.incCost })}>
<td className='ptr' style={{ width: '1em' }} onClick={toggle}>{item.m.class + item.m.rating}</td>
<td className='le ptr shorten cap' onClick={toggle}>{slotName(translate, item)}</td>
<td className='ri ptr' onClick={toggle}>{formats.int(item.discountedCost)}{units.CR}</td>
<td className='ptr' style={{ width: '1em' }} onTouchTap={toggle}>{item.m.class + item.m.rating}</td>
<td className='le ptr shorten cap' onTouchTap={toggle}>{slotName(translate, item)}</td>
<td className='ri ptr' onTouchTap={toggle}>{formats.int(item.discountedCost)}{units.CR}</td>
</tr>);
}
}
@@ -306,12 +306,12 @@ export default class CostSection extends TranslatedComponent {
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr className='main'>
<th colSpan='2' className='sortable le' onClick={this._sortCostBy.bind(this,'m')}>
<th colSpan='2' className='sortable le' onTouchTap={this._sortCostBy.bind(this,'m')}>
{translate('component')}
{shipDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct1(1 - shipDiscount)}]`}</u>}
{moduleDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct1(1 - moduleDiscount)}]`}</u>}
</th>
<th className='sortable le' onClick={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
<th className='sortable le' onTouchTap={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
</tr>
</thead>
<tbody>
@@ -346,7 +346,7 @@ export default class CostSection extends TranslatedComponent {
if (retrofitCosts.length) {
for (let i = 0, l = retrofitCosts.length; i < l; i++) {
let item = retrofitCosts[i];
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onClick={this._toggleRetrofitCost.bind(this, item)}>
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onTouchTap={this._toggleRetrofitCost.bind(this, item)}>
<td style={{ width: '1em' }}>{item.sellClassRating}</td>
<td className='le shorten cap'>{translate(item.sellName)}</td>
<td style={{ width: '1em' }}>{item.buyClassRating}</td>
@@ -363,9 +363,9 @@ export default class CostSection extends TranslatedComponent {
<table style={{ width: '100%' }}>
<thead>
<tr className='main'>
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'sellName')}>{translate('sell')}</th>
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'buyName')}>{translate('buy')}</th>
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'cr')}>
<th colSpan='2' className='sortable le' onTouchTap={this._sortRetrofitBy.bind(this, 'sellName')}>{translate('sell')}</th>
<th colSpan='2' className='sortable le' onTouchTap={this._sortRetrofitBy.bind(this, 'buyName')}>{translate('buy')}</th>
<th colSpan='2' className='sortable le' onTouchTap={this._sortRetrofitBy.bind(this, 'cr')}>
{translate('net cost')}
{moduleDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.rPct(1 - moduleDiscount)}]`}</u>}
</th>
@@ -473,10 +473,10 @@ export default class CostSection extends TranslatedComponent {
<table style={{ width: '100%' }}>
<thead>
<tr className='main'>
<th colSpan='2' className='sortable le' onClick={this._sortAmmoBy.bind(this, 'm')} >{translate('module')}</th>
<th colSpan='1' className='sortable le' onClick={this._sortAmmoBy.bind(this, 'max')} >{translate('qty')}</th>
<th colSpan='1' className='sortable le' onClick={this._sortAmmoBy.bind(this, 'cost')} >{translate('unit cost')}</th>
<th className='sortable le' onClick={this._sortAmmoBy.bind(this, 'total')}>{translate('total cost')}</th>
<th colSpan='2' className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'm')} >{translate('module')}</th>
<th colSpan='1' className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'max')} >{translate('qty')}</th>
<th colSpan='1' className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'cost')} >{translate('unit cost')}</th>
<th className='sortable le' onTouchTap={this._sortAmmoBy.bind(this, 'total')}>{translate('total cost')}</th>
</tr>
</thead>
<tbody>
@@ -673,9 +673,9 @@ export default class CostSection extends TranslatedComponent {
<table className='tabs'>
<thead>
<tr>
<th style={{ width:'33%' }} className={cn({ active: tab == 'costs' })} onClick={this._showTab.bind(this, 'costs')} >{translate('costs')}</th>
<th style={{ width:'33%' }} className={cn({ active: tab == 'retrofit' })} onClick={this._showTab.bind(this, 'retrofit')} >{translate('retrofit costs')}</th>
<th style={{ width:'34%' }} className={cn({ active: tab == 'ammo' })} onClick={this._showTab.bind(this, 'ammo')} >{translate('reload costs')}</th>
<th style={{ width:'33%' }} className={cn({ active: tab == 'costs' })} onTouchTap={this._showTab.bind(this, 'costs')} >{translate('costs')}</th>
<th style={{ width:'33%' }} className={cn({ active: tab == 'retrofit' })} onTouchTap={this._showTab.bind(this, 'retrofit')} >{translate('retrofit costs')}</th>
<th style={{ width:'34%' }} className={cn({ active: tab == 'ammo' })} onTouchTap={this._showTab.bind(this, 'ammo')} >{translate('reload costs')}</th>
</tr>
</thead>
</table>

View File

@@ -3,6 +3,7 @@ import SlotSection from './SlotSection';
import HardpointSlot from './HardpointSlot';
import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
/**
* Hardpoint slot section
@@ -90,39 +91,39 @@ export default class HardpointsSlotSection extends SlotSection {
_getSectionMenu(translate) {
let _fill = this._fill;
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()}>
return <div className='select hardpoint' onTouchTap={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
<li className='lc' onTouchTap={this._empty}>{translate('empty all')}</li>
</ul>
<div className='select-group cap'>{translate('pl')}</div>
<ul>
<li className='c' onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
</ul>
<div className='select-group cap'>{translate('ul')}</div>
<ul>
<li className='c' onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
</ul>
<div className='select-group cap'>{translate('bl')}</div>
<ul>
<li className='c' onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
</ul>
<div className='select-group cap'>{translate('mc')}</div>
<ul>
<li className='c' onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
</ul>
<div className='select-group cap'>{translate('c')}</div>
<ul>
<li className='c' onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
<li className='c' onTouchTap={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
</ul>
</div>;
}

View File

@@ -34,6 +34,11 @@ export default class Header extends TranslatedComponent {
this._setLanguage = this._setLanguage.bind(this);
this._setInsurance = this._setInsurance.bind(this);
this._openShips = this._openMenu.bind(this, 's');
this._openBuilds = this._openMenu.bind(this, 'b');
this._openComp = this._openMenu.bind(this, 'comp');
this._openSettings = this._openMenu.bind(this, 'settings');
this.languageOptions = [];
this.insuranceOptions = [];
this.discountOptions = [];
@@ -156,12 +161,11 @@ export default class Header extends TranslatedComponent {
/**
* Open a menu
* @param {SyntheticEvent} event Event
* @param {string} menu Menu name
* @param {SyntheticEvent} event Event
*/
_openMenu(event, menu) {
_openMenu(menu, event) {
event.stopPropagation();
if (this.props.currentMenu == menu) {
menu = null;
}
@@ -181,7 +185,7 @@ export default class Header extends TranslatedComponent {
}
return (
<div className='menu-list dbl no-wrap' onClick={ (e) => e.stopPropagation() }>
<div className='menu-list dbl no-wrap' onTouchTap={ (e) => e.stopPropagation() }>
{shipList}
</div>
);
@@ -207,7 +211,7 @@ export default class Header extends TranslatedComponent {
}
return (
<div className='menu-list' onClick={ (e) => e.stopPropagation() }>
<div className='menu-list' onTouchTap={ (e) => e.stopPropagation() }>
<div className='dbl'>{buildList}</div>
</div>
);
@@ -233,7 +237,7 @@ export default class Header extends TranslatedComponent {
}
return (
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
<div className='menu-list' onTouchTap={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
{comparisons}
<hr />
<Link href='/compare/all' ui-sref="compare({name: 'all'})" className='block cap'>{translate('compare all')}</Link>
@@ -251,14 +255,14 @@ export default class Header extends TranslatedComponent {
let tips = Persist.showTooltips();
return (
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
<div className='menu-list no-wrap cap' onTouchTap={ (e) => e.stopPropagation() }>
<div style={{ lineHeight: '2em' }}>
{translate('language')}
<select className='cap' value={Persist.getLangCode()} onChange={this._setLanguage}>
{this.languageOptions}
</select>
<br/>
<span className='cap ptr' onClick={this._toggleTooltips} >
<span className='cap ptr' onTouchTap={this._toggleTooltips} >
{translate('tooltips')}
<div className={cn({ disabled: !tips, 'primary-disabled': tips })} style={{ marginLeft: '0.5em', display: 'inline-block' }}>{(tips ? '✓' : '✗')}</div>
</span>
@@ -281,10 +285,10 @@ export default class Header extends TranslatedComponent {
<hr />
<ul>
{translate('builds')} & {translate('comparisons')}
<li><a href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</a></li>
<li><a href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</a></li>
<li><a href="#" className='block' onClick={this._showImport.bind(this)}>{translate('import')}</a></li>
<li><a href="#" onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</a></li>
<li><a href="#" className='block' onTouchTap={this._showBackup.bind(this)}>{translate('backup')}</a></li>
<li><a href="#" className='block' onTouchTap={this._showDetailedExport.bind(this)}>{translate('detailed export')}</a></li>
<li><a href="#" className='block' onTouchTap={this._showImport.bind(this)}>{translate('import')}</a></li>
<li><a href="#" onTouchTap={this._showDeleteAll.bind(this)}>{translate('delete all')}</a></li>
</ul>
<hr />
<table style={{ width: 300, backgroundColor: 'transparent' }}>
@@ -295,7 +299,7 @@ export default class Header extends TranslatedComponent {
<td style={{ width: 20 }}><span style={{ fontSize: 30 }}>A</span></td>
</tr>
<tr>
<td colSpan='3' style={{ textAlign: 'center', cursor: 'pointer' }} className='primary-disabled cap' onClick={this._resetTextSize.bind(this)}>{translate('reset')}</td>
<td colSpan='3' style={{ textAlign: 'center', cursor: 'pointer' }} className='primary-disabled cap' onTouchTap={this._resetTextSize.bind(this)}>{translate('reset')}</td>
</tr>
</tbody>
</table>
@@ -343,7 +347,7 @@ export default class Header extends TranslatedComponent {
let hasBuilds = Persist.hasBuilds();
if (this.props.appCacheUpdate) {
return <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
return <div id="app-update" onTouchTap={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
}
return (
@@ -351,28 +355,28 @@ export default class Header extends TranslatedComponent {
<Link className='l' href="/" style={{ marginRight: '1em' }} title="Home"><CoriolisLogo className='icon xl' /></Link>
<div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 's' })} onClick={ (e) => this._openMenu(e,'s') } >
<div className={cn('menu-header', { selected: openedMenu == 's' })} onTouchTap={this._openShips}>
<Rocket className='warning' /><span className='menu-item-label'>{' ' + translate('ships')}</span>
</div>
{openedMenu == 's' ? this._getShipsMenu() : null}
</div>
<div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 'b', disabled: !hasBuilds })} onClick={ hasBuilds ? (e) => this._openMenu(e,'b') : null }>
<div className={cn('menu-header', { selected: openedMenu == 'b', disabled: !hasBuilds })} onTouchTap={hasBuilds && this._openBuilds}>
<Hammer className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{' ' + translate('builds')}</span>
</div>
{openedMenu == 'b' ? this._getBuildsMenu() : null}
</div>
<div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onClick={ hasBuilds ? (e) => this._openMenu(e,'comp') : null }>
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onTouchTap={hasBuilds && this._openComp}>
<StatsBars className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{' ' + translate('compare')}</span>
</div>
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
</div>
<div className='r menu'>
<div className={cn('menu-header', { selected: openedMenu == 'settings' })}onClick={ (e) => this._openMenu(e,'settings') }>
<div className={cn('menu-header', { selected: openedMenu == 'settings' })} onTouchTap={this._openSettings}>
<Cogs className='xl warning'/><span className='menu-item-label'>{translate('settings')}</span>
</div>
{openedMenu == 'settings' ? this._getSettingsMenu() : null}

View File

@@ -3,6 +3,7 @@ import cn from 'classnames';
import SlotSection from './SlotSection';
import InternalSlot from './InternalSlot';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
/**
* Internal slot section
@@ -131,12 +132,12 @@ export default class InternalSlotSection extends SlotSection {
* @return {React.Component} Section menu
*/
_getSectionMenu(translate) {
return <div className='select' onClick={e => e.stopPropagation()}>
return <div className='select' onTouchTap={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
<li className='lc' onTouchTap={this._empty}>{translate('empty all')}</li>
<li className='lc' onTouchTap={this._fillWithCargo}>{translate('cargo')}</li>
<li className='lc' onTouchTap={this._fillWithCells}>{translate('scb')}</li>
<li className='lc' onTouchTap={this._fillWithArmor}>{translate('hr')}</li>
</ul>
</div>;
}

View File

@@ -1,5 +1,4 @@
import React from 'react';
import { findDOMNode } from 'react-dom';
import d3 from 'd3';
import TranslatedComponent from './TranslatedComponent';

View File

@@ -41,7 +41,8 @@ export default class Link extends React.Component {
* @return {React.Component} A href element
*/
render() {
return <a {...this.props} onClick={this.handler.bind(this)}>{this.props.children}</a>;
let action = this.handler.bind(this);
return <a {...this.props} onTouchTap={action}>{this.props.children}</a>;
}
}

View File

@@ -96,20 +96,20 @@ export default class ModalCompare extends TranslatedComponent {
let translate = this.context.language.translate;
let availableBuilds = unusedBuilds.map((build, i) =>
<tr key={i} onClick={this._addBuild.bind(this, i)}>
<tr key={i} onTouchTap={this._addBuild.bind(this, i)}>
<td className='tl'>{build.name}</td>
<td className='tl'>{build.buildName}</td>
</tr>
);
let selectedBuilds = usedBuilds.map((build, i) =>
<tr key={i} onClick={this._removeBuild.bind(this, i)}>
<tr key={i} onTouchTap={this._removeBuild.bind(this, i)}>
<td className='tl'>{build.name}</td><
td className='tl'>{build.buildName}</td>
</tr>
);
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() }>
<h3>{translate('PHRASE_SELECT_BUILDS')}</h3>
<div id='build-select'>
<table>
@@ -127,8 +127,8 @@ export default class ModalCompare extends TranslatedComponent {
</table>
</div>
<br/>
<button className='cap' onClick={this._selectBuilds.bind(this)}>{translate('Ok')}</button>
<button className='r cap' onClick={() => this.context.hideModal()}>{translate('Cancel')}</button>
<button className='cap' onTouchTap={this._selectBuilds.bind(this)}>{translate('Ok')}</button>
<button className='r cap' onTouchTap={() => this.context.hideModal()}>{translate('Cancel')}</button>
</div>;
}
}

View File

@@ -22,11 +22,11 @@ export default class ModalDeleteAll extends TranslatedComponent {
render() {
let translate = this.context.language.translate;
return <div className='modal' onClick={(e) => e.stopPropagation()}>
return <div className='modal' onTouchTap={(e) => e.stopPropagation()}>
<h2>{translate('delete all')}</h2>
<p className='cen'>{translate('PHRASE_CONFIRMATION')}</p>
<button className='l cap' onClick={this._deleteAll.bind(this)}>{translate('yes')}</button>
<button className='r cap' onClick={this.context.hideModal}>{translate('no')}</button>
<button className='l cap' onTouchTap={this._deleteAll.bind(this)}>{translate('yes')}</button>
<button className='r cap' onTouchTap={this.context.hideModal}>{translate('no')}</button>
</div>;
}
}

View File

@@ -52,13 +52,13 @@ export default class ModalExport extends TranslatedComponent {
description = <div>{translate(this.props.description)}</div>;
}
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() }>
<h2>{translate(this.props.title || 'Export')}</h2>
{description}
<div>
<textarea className='cb json' onFocus={ (e) => e.target.select() } readOnly value={this.state.exportJson} />
</div>
<button className='r dismiss cap' onClick={this.context.hideModal}>{translate('close')}</button>
<button className='r dismiss cap' onTouchTap={this.context.hideModal}>{translate('close')}</button>
</div>;
}
}

View File

@@ -89,6 +89,11 @@ function detailedJsonToBuild(detailedBuild) {
*/
export default class ModalImport extends TranslatedComponent {
static propTypes = {
builds: React.PropTypes.object, // Optional: Import object
};
/**
* Constructor
* @param {Object} props React Component properties
@@ -97,8 +102,8 @@ export default class ModalImport extends TranslatedComponent {
super(props);
this.state = {
builds: null,
canEdit: true,
builds: props.builds,
canEdit: !props.builds,
comparisons: null,
discounts: null,
errorMsg: null,
@@ -326,13 +331,13 @@ export default class ModalImport extends TranslatedComponent {
let builds = null, comparisons = null;
if (this.state.builds) {
builds = this.state.builds;
for (let shipId in builds) {
for (let buildName in builds[shipId]) {
let code = builds[shipId][buildName];
// Update builds object such that orginal name retained, but can be renamed
builds = {}; // Create new builds object such that orginal name retained, but can be renamed
for (let shipId in this.state.builds) {
let shipbuilds = this.state.builds[shipId];
builds[shipId] = {};
for (let buildName in shipbuilds) {
builds[shipId][buildName] = {
code,
code: shipbuilds[buildName],
useName: buildName
};
}
@@ -340,9 +345,9 @@ export default class ModalImport extends TranslatedComponent {
}
if (this.state.comparisons) {
comparisons = this.state.comparisons;
for (let name in comparisons) {
comparisons[name].useName = name;
comparisons = {};
for (let name in this.state.comparisons) {
comparisons[name] = Object.assign({ useName: name }, this.state.comparisons[name]);
}
}
@@ -403,8 +408,7 @@ export default class ModalImport extends TranslatedComponent {
* If imported data is already provided process immediately on mount
*/
componentWillMount() {
if (this.props.importingBuilds) {
this.setState({ builds: this.props.importingBuilds, canEdit : false });
if (this.props.builds) {
this._process();
}
}
@@ -504,7 +508,7 @@ export default class ModalImport extends TranslatedComponent {
);
}
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() } onClick={ (e) => e.stopPropagation() }>
<h2 >{translate('import')}</h2>
{importStage}
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>

View File

@@ -40,7 +40,7 @@ export default class ModalPermalink extends TranslatedComponent {
render() {
let translate = this.context.language.translate;
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
return <div className='modal' onTouchTap={ (e) => e.stopPropagation() }>
<h2>{translate('permalink')}</h2>
<br/>
<h3>{translate('URL')}</h3>
@@ -49,7 +49,7 @@ export default class ModalPermalink extends TranslatedComponent {
<h3 >{translate('shortened')}</h3>
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<br/><br/>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
<button className={'r dismiss cap'} onTouchTap={this.context.hideModal}>{translate('close')}</button>
</div>;
}
}

View File

@@ -211,7 +211,7 @@ export default class PowerBands extends TranslatedComponent {
height={state.barHeight}
x={Math.floor(Math.max(wattScale(b.retractedSum) - wattScale(b.retracted), 0))}
y={1}
onClick={this._selectRet.bind(this, i)}
onTouchTap={this._selectRet.bind(this, i)}
className={getClass(ret[i], b.retractedSum, available)}
/>);
@@ -223,7 +223,7 @@ export default class PowerBands extends TranslatedComponent {
height={state.barHeight}
x={wattScale(b.retractedSum) - (wattScale(b.retracted) / 2)}
y={state.retY}
onClick={this._selectRet.bind(this, i)}
onTouchTap={this._selectRet.bind(this, i)}
className='primary-bg'>{retLbl}</text>
);
}
@@ -238,7 +238,7 @@ export default class PowerBands extends TranslatedComponent {
height={state.barHeight}
x={Math.floor(Math.max(wattScale(b.deployedSum) - wattScale(b.retracted) - wattScale(b.deployed), 0))}
y={state.barHeight + 1}
onClick={this._selectDep.bind(this, i)}
onTouchTap={this._selectDep.bind(this, i)}
className={getClass(dep[i], b.deployedSum, available)}
/>);
@@ -250,7 +250,7 @@ export default class PowerBands extends TranslatedComponent {
height={state.barHeight}
x={wattScale(b.deployedSum) - ((wattScale(b.retracted) + wattScale(b.deployed)) / 2)}
y={state.depY}
onClick={this._selectDep.bind(this, i)}
onTouchTap={this._selectDep.bind(this, i)}
className='primary-bg'>{depLbl}</text>
);
}

View File

@@ -119,23 +119,23 @@ export default class PowerManagement extends TranslatedComponent {
let retractedElem = null, deployedElem = null;
if (slot.enabled) {
retractedElem = <td className='ptr upp' onClick={toggleEnabled}>{POWER[ship.getSlotStatus(slot, false)]}</td>;
deployedElem = <td className='ptr upp' onClick={toggleEnabled}>{POWER[ship.getSlotStatus(slot, true)]}</td>;
retractedElem = <td className='ptr upp' onTouchTap={toggleEnabled}>{POWER[ship.getSlotStatus(slot, false)]}</td>;
deployedElem = <td className='ptr upp' onTouchTap={toggleEnabled}>{POWER[ship.getSlotStatus(slot, true)]}</td>;
} else {
retractedElem = <td className='ptr disabled upp' colSpan='2' onClick={toggleEnabled}>{translate('disabled')}</td>;
retractedElem = <td className='ptr disabled upp' colSpan='2' onTouchTap={toggleEnabled}>{translate('disabled')}</td>;
}
powerRows.push(<tr key={i} className={cn('highlight', { disabled: !slot.enabled })}>
<td className='ptr' style={{ width: '1em' }} onClick={toggleEnabled}>{m.class + m.rating}</td>
<td className='ptr le shorten cap' onClick={toggleEnabled}>{slotName(translate, slot)}</td>
<td className='ptr' onClick={toggleEnabled}><u>{translate(slot.type)}</u></td>
<td className='ptr' style={{ width: '1em' }} onTouchTap={toggleEnabled}>{m.class + m.rating}</td>
<td className='ptr le shorten cap' onTouchTap={toggleEnabled}>{slotName(translate, slot)}</td>
<td className='ptr' onTouchTap={toggleEnabled}><u>{translate(slot.type)}</u></td>
<td>
<span className='flip ptr btn' onClick={this._priority.bind(this, slot, -1)}>&#9658;</span>
<span className='flip ptr btn' onTouchTap={this._priority.bind(this, slot, -1)}>&#9658;</span>
{' ' + (slot.priority + 1) + ' '}
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>&#9658;</span>
<span className='ptr btn' onTouchTap={this._priority.bind(this, slot, 1)}>&#9658;</span>
</td>
<td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.power)}</td>
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
<td className='ri ptr' style={{ width: '3.25em' }} onTouchTap={toggleEnabled}>{pwr(m.power)}</td>
<td className='ri ptr' style={{ width: '3em' }} onTouchTap={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
{retractedElem}
{deployedElem}
</tr>);
@@ -200,12 +200,12 @@ export default class PowerManagement extends TranslatedComponent {
<table style={{ width: '100%' }}>
<thead>
<tr className='main'>
<th colSpan='2' className='sortable le' onClick={sortOrder.bind(this, 'n')} >{translate('module')}</th>
<th style={{ width: '3em' }} className='sortable' onClick={sortOrder.bind(this, 't')} >{translate('type')}</th>
<th style={{ width: '4em' }} className='sortable' onClick={sortOrder.bind(this, 'pri')} >{translate('pri')}</th>
<th colSpan='2' className='sortable' onClick={sortOrder.bind(this, 'pwr')} >{translate('PWR')}</th>
<th style={{ width: '3em' }} className='sortable' onClick={sortOrder.bind(this, 'r')} >{translate('ret')}</th>
<th style={{ width: '3em' }} className='sortable' onClick={sortOrder.bind(this, 'd')} >{translate('dep')}</th>
<th colSpan='2' className='sortable le' onTouchTap={sortOrder.bind(this, 'n')} >{translate('module')}</th>
<th style={{ width: '3em' }} className='sortable' onTouchTap={sortOrder.bind(this, 't')} >{translate('type')}</th>
<th style={{ width: '4em' }} className='sortable' onTouchTap={sortOrder.bind(this, 'pri')} >{translate('pri')}</th>
<th colSpan='2' className='sortable' onTouchTap={sortOrder.bind(this, 'pwr')} >{translate('PWR')}</th>
<th style={{ width: '3em' }} className='sortable' onTouchTap={sortOrder.bind(this, 'r')} >{translate('ret')}</th>
<th style={{ width: '3em' }} className='sortable' onTouchTap={sortOrder.bind(this, 'd')} >{translate('dep')}</th>
</tr>
</thead>
<tbody>

View File

@@ -1,4 +1,7 @@
import React from 'react';
import { findDOMNode } from 'react-dom';
const MARGIN_LR = 8; // Left/ Right margin
/**
* Horizontal Slider
@@ -8,7 +11,8 @@ export default class Slider extends React.Component {
static defaultProps = {
axis: false,
min: 0,
max: 1
max: 1,
scale: 1 // SVG render scale
};
static PropTypes = {
@@ -16,6 +20,7 @@ export default class Slider extends React.Component {
axisUnit: React.PropTypes.string,
min: React.PropTypes.number,
max: React.PropTypes.number,
scale: React.PropTypes.number,
onChange: React.PropTypes.func.isRequired,
};
@@ -25,52 +30,101 @@ export default class Slider extends React.Component {
*/
constructor(props) {
super(props);
this._down = this._down.bind(this);
this._move = this._move.bind(this);
this._up = this._up.bind(this);
this._updatePercent = this._updatePercent.bind(this);
this._updateDimensions = this._updateDimensions.bind(this);
this.down = this.down.bind(this);
this.up = this.up.bind(this);
this.state = { width: 0 };
}
/**
* On Mouse down handler
* On Mouse/Touch down handler
* @param {SyntheticEvent} event Event
*/
down(event) {
if (this.move) {
this.up(event);
} else {
let rect = event.currentTarget.getBoundingClientRect();
this.move = this._updatePercent.bind(this, rect.left, rect.width);
this.move(event);
document.addEventListener('mousemove', this.move, true);
document.addEventListener('mouseup', this.up, true);
_down(event) {
let rect = event.currentTarget.getBoundingClientRect();
this.left = rect.left;
this.width = rect.width;
this._move(event);
}
/**
* Update the slider percentage on move
* @param {SyntheticEvent} event Event
*/
_move(event) {
if(this.width !== null && this.left != null) {
let clientX = event.touches ? event.touches[0].clientX : event.clientX;
event.preventDefault();
this._updatePercent(clientX - this.left, this.width);
}
}
/**
* On Mouse up handler
* On Mouse/Touch up handler
* @param {Event} event DOM Event
*/
up(event) {
document.removeEventListener('mousemove', this.move, true);
document.removeEventListener('mouseup', this.up, true);
this.move = null;
_up(event) {
event.preventDefault();
this.left = null;
this.width = null;
}
/**
* Determine if the user is still dragging
* @param {SyntheticEvent} event Event
*/
_enter(event) {
if(event.buttons !== 1) {
this.left = null;
this.width = null;
}
}
/**
* Update the slider percentage
* @param {number} left Slider left position
* @param {number} pos Slider drag position
* @param {number} width Slider width
* @param {Event} event DOM Event
*/
_updatePercent(left, width, event) {
this.props.onChange(Math.min(Math.max((event.clientX - left) / width, 0), 1));
_updatePercent(pos, width) {
this.props.onChange(Math.min(Math.max(pos / width, 0), 1));
}
/**
* Update dimenions from rendered DOM
*/
_updateDimensions() {
this.setState({
outerWidth: findDOMNode(this).offsetWidth
});
}
/**
* Add listeners when about to mount
*/
componentWillMount() {
if (this.props.onResize) {
this.resizeListener = this.props.onResize(this._updateDimensions);
}
}
/**
* Trigger DOM updates on mount
*/
componentDidMount() {
this._updateDimensions();
}
/**
* Remove listeners on unmount
*/
componentWillUnmount() {
this.up();
if (this.resizeListener) {
this.resizeListener.remove();
}
}
/**
@@ -78,24 +132,33 @@ export default class Slider extends React.Component {
* @return {React.Component} The slider
*/
render() {
let pctStr = (this.props.percent * 100) + '%';
let { axis, axisUnit, min, max } = this.props;
let axisGroup;
let outerWidth = this.state.outerWidth;
let { axis, axisUnit, min, max, scale } = this.props;
if (axis) {
axisGroup = <g style={{ fontSize: '.7em' }}>
<text className='primary-disabled' y='3em' x='0' style={{ textAnchor: 'middle' }}>{min + axisUnit}</text>
<text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
<text className='primary-disabled' y='3em' x='99%' style={{ textAnchor: 'middle' }}>{max + axisUnit}</text>
</g>;
let style = {
width: '100%',
height: axis ? '2.5em' : '1.5em',
boxSizing: 'border-box'
};
if (!outerWidth) {
return <svg style={style} />;
}
return <svg style={{ width: '100%', height: axis ? '2.5em' : '1.5em', padding: '0 0.6em', cursor: 'col-resize', boxSizing: 'border-box' }}>
<rect className='primary' style={{ opacity: 0.3 }} y='0.25em' rx='0.3em' ry='0.3em' width='100%' height='0.7em' />
<rect className='primary-disabled'y='0.45em' rx='0.15em' ry='0.15em' width={pctStr} height='0.3em' />
<circle className='primary' r='0.6em' cy='0.6em' cx={pctStr} />
<rect width='100%' height='100%' fillOpacity='0' onMouseDown={this.down} onClick={this.click} />
{axisGroup}
let margin = MARGIN_LR * scale;
let width = outerWidth - (margin * 2);
let pctPos = width * this.props.percent;
return <svg onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onTouchEnd={this._up} style={style}>
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
<rect x={margin} width={width} height='100%' fillOpacity='0' style={{ cursor: 'col-resize' }} onMouseDown={this._down} onTouchMove={this._move} onTouchStart={this._down} />
{axis && <g style={{ fontSize: '.7em' }}>
<text className='primary-disabled' y='3em' x={margin} style={{ textAnchor: 'middle' }}>{min + axisUnit}</text>
<text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
</g>}
</svg>;
}
}

View File

@@ -92,8 +92,10 @@ export default class Slot extends TranslatedComponent {
/>;
}
// TODO: implement touch dragging
return (
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
<div className={cn('slot', dropClass, { selected })} onTouchTap={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
<div className='details-container'>
<div className='sz'>{this._getMaxClassLabel(translate)}</div>
{slotDetails}

View File

@@ -49,11 +49,11 @@ export default class SlotSection extends TranslatedComponent {
* @param {SyntheticEvent} event Event
*/
_openMenu(menu, event) {
event.preventDefault();
event.stopPropagation();
if (this.props.currentMenu === menu) {
menu = null;
}
this.context.openMenu(menu);
}
@@ -90,7 +90,6 @@ export default class SlotSection extends TranslatedComponent {
e.stopPropagation();
let os = this.state.originSlot;
if (os) {
console.log('has origin');
e.dataTransfer.dropEffect = os != targetSlot && targetSlot.maxClass >= os.m.class ? 'copyMove' : 'none';
this.setState({ targetSlot });
} else {
@@ -157,16 +156,6 @@ export default class SlotSection extends TranslatedComponent {
return 'ineligible'; // Cannot be dropped / invalid drop slot
}
/**
* Toggle slot Active/Inactive
* @param {Object} slot Slot
*/
_togglePwr(slot) {
this.props.ship.setSlotEnabled(slot, !slot.enabled);
this.props.onChange();
this._close();
}
/**
* Close current menu
*/
@@ -188,7 +177,7 @@ export default class SlotSection extends TranslatedComponent {
return (
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
<div className={cn('section-menu', { selected: sectionMenuOpened })} onTouchTap={open} onContextMenu={ctx}>
<h1>{translate(this.sectionName)} <Equalizer/></h1>
{sectionMenuOpened ? this._getSectionMenu(translate) : null }
</div>

View File

@@ -43,7 +43,7 @@ export default class StandardSlot extends TranslatedComponent {
}
return (
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen}>
<div className={cn('slot', { selected: this.props.selected })} onTouchTap={this.props.onOpen}>
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
<div className={'sz'}>{slot.maxClass}</div>
<div>

View File

@@ -4,6 +4,7 @@ import SlotSection from './SlotSection';
import StandardSlot from './StandardSlot';
import { diffDetails } from '../utils/SlotFunctions';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
/**
* Standard Slot section
@@ -171,7 +172,7 @@ export default class StandardSlotSection extends SlotSection {
let bh = ship.bulkheads;
slots[0] = (
<div key='bh' className={cn('slot', { selected: currentMenu === bh })} onClick={open.bind(this, bh)}>
<div key='bh' className={cn('slot', { selected: currentMenu === bh })} onTouchTap={open.bind(this, bh)}>
<div className={'details-container'}>
<div className={'details'}>
<div className={'sz'}>8</div>
@@ -183,21 +184,21 @@ export default class StandardSlotSection extends SlotSection {
</div>
</div>
{currentMenu === bh &&
<div className='select' onClick={ e => e.stopPropagation() }>
<div className='select' onTouchTap={ e => e.stopPropagation() }>
<ul>
<li onClick={selBulkhead.bind(this, 0)} onMouseOver={this._bhDiff.bind(this, 0)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 0 })}>
<li onTouchTap={selBulkhead.bind(this, 0)} onMouseOver={this._bhDiff.bind(this, 0)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 0 })}>
{translate('Lightweight Alloy')}
</li>
<li onClick={selBulkhead.bind(this, 1)} onMouseOver={this._bhDiff.bind(this, 1)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 1 })}>
<li onTouchTap={selBulkhead.bind(this, 1)} onMouseOver={this._bhDiff.bind(this, 1)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 1 })}>
{translate('Reinforced Alloy')}
</li>
<li onClick={selBulkhead.bind(this, 2)} onMouseOver={this._bhDiff.bind(this, 2)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 2 })}>
<li onTouchTap={selBulkhead.bind(this, 2)} onMouseOver={this._bhDiff.bind(this, 2)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 2 })}>
{translate('Military Grade Composite')}
</li>
<li onClick={selBulkhead.bind(this, 3)} onMouseOver={this._bhDiff.bind(this, 3)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 3 })}>
<li onTouchTap={selBulkhead.bind(this, 3)} onMouseOver={this._bhDiff.bind(this, 3)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 3 })}>
{translate('Mirrored Surface Composite')}
</li>
<li onClick={selBulkhead.bind(this, 4)} onMouseOver={this._bhDiff.bind(this, 4)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 4 })}>
<li onTouchTap={selBulkhead.bind(this, 4)} onMouseOver={this._bhDiff.bind(this, 4)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 4 })}>
{translate('Reactive Surface Composite')}
</li>
</ul>
@@ -293,19 +294,19 @@ export default class StandardSlotSection extends SlotSection {
_getSectionMenu(translate) {
let _fill = this._fill;
return <div className='select' onClick={(e) => e.stopPropagation()}>
return <div className='select' onTouchTap={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul>
<li className='lc' onClick={this._optimizeStandard}>{translate('Optimize')}</li>
<li className='c' onClick={_fill.bind(this, 'E')}>E</li>
<li className='c' onClick={_fill.bind(this, 'D')}>D</li>
<li className='c' onClick={_fill.bind(this, 'C')}>C</li>
<li className='c' onClick={_fill.bind(this, 'B')}>B</li>
<li className='c' onClick={_fill.bind(this, 'A')}>A</li>
<li className='lc' onTouchTap={this._optimizeStandard}>{translate('Optimize')}</li>
<li className='c' onTouchTap={_fill.bind(this, 'E')}>E</li>
<li className='c' onTouchTap={_fill.bind(this, 'D')}>D</li>
<li className='c' onTouchTap={_fill.bind(this, 'C')}>C</li>
<li className='c' onTouchTap={_fill.bind(this, 'B')}>B</li>
<li className='c' onTouchTap={_fill.bind(this, 'A')}>A</li>
</ul>
<div className='select-group cap'>{translate('builds / roles')}</div>
<ul>
<li className='lc' onClick={this._optimizeCargo}>{translate('Trader')}</li>
<li className='lc' onClick={this._optimizeExplorer}>{translate('Explorer')}</li>
<li className='lc' onTouchTap={this._optimizeCargo}>{translate('Trader')}</li>
<li className='lc' onTouchTap={this._optimizeExplorer}>{translate('Explorer')}</li>
</ul>
</div>;
}

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { shallowEqual } from '../utils/UtilityFunctions';
import TranslatedComponent from './TranslatedComponent';
/**
* Document Root Tooltip
*/
export default class Tooltip extends React.Component {
export default class Tooltip extends TranslatedComponent {
static propTypes = {
rect: React.PropTypes.object.isRequired,
@@ -16,36 +16,98 @@ export default class Tooltip extends React.Component {
};
/**
* Adjusts the position of the tooltip if its content
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this._adjustDimensions = this._adjustDimensions.bind(this);
this.state = this._initialDimensions(props);
}
/**
* Get position and reset width/height
* @param {Object} props React Component properties
* @return {Object} Dimenions / state
*/
_initialDimensions(props) {
let { options, rect } = props;
let orientation = options.orientation || 'n';
let top, left;
switch (orientation) {
case 's':
top = Math.round(rect.top + rect.height);
left = Math.round(rect.left + (rect.width / 2));
break;
case 'n':
top = Math.round(rect.top);
left = Math.round(rect.left + (rect.width / 2));
break;
case 'e':
top = Math.round(rect.top + (rect.height / 2));
left = Math.round(rect.left + rect.width);
break;
case 'w':
top = Math.round(rect.top + (rect.height / 2));
left = Math.round(rect.left);
}
return { top, left, arrLeft: left, width: null, height: null, orientation };
}
/**
* Adjusts the position and size of the tooltip if its content
* appear outside of the windows left or right border
* @param {DomElement} elem Tooltip contents container
*/
_adjustPosition(elem) {
if (elem) {
let o = this.props.options.orientation || 'n';
let rect = elem.getBoundingClientRect();
_adjustDimensions() {
if (this.elem) {
let o = this.state.orientation;
let rect = this.elem.getBoundingClientRect();
// Round widthand height to nearest even number to avoid translate3d text blur
// caused by fractional pixels
let width = Math.ceil(rect.width / 2) * 2;
this.setState({
width,
height: Math.round(rect.height / 2) * 2
});
if (o == 'n' || o == 's') {
let docWidth = document.documentElement.clientWidth;
if (rect.left < 0) {
elem.style.left = rect.width / 2 + 'px';
} else if ((rect.left + rect.width) > docWidth) {
elem.style.left = docWidth - (rect.width / 2) + 'px';
this.setState({ left: Math.round(width / 4) * 2 });
} else if ((rect.left + width) > docWidth) {
this.setState({ left: docWidth - Math.round(width / 4) * 2 });
}
}
}
}
/**
* Determine if a component should be rerendered
* @param {object} nextProps Next properties
* @return {boolean} true if update is needed
*Potentially adjust component dimensions after mount
*/
shouldComponentUpdate(nextProps) {
return !shallowEqual(this.props, nextProps);
componentDidMount() {
this._adjustDimensions();
}
/**
* Reset width and height on propChange
* @param {Object} nextProps Incoming/Next properties
*/
componentWillReceiveProps(nextProps) {
this.setState(this._initialDimensions(nextProps));
}
/**
* Potentially adjust component dimensions on re-render
*/
componentDidUpdate() {
this._adjustDimensions();
}
/**
* Renders the component
@@ -55,33 +117,12 @@ export default class Tooltip extends React.Component {
if (!this.props.children) { // If no content is provided
return null;
}
let { top, left, arrLeft, width, height, orientation } = this.state;
let { children, options, rect } = this.props;
let o = options.orientation || 'n';
let style = options.style || {};
switch (o) {
case 's':
style.top = rect.top + rect.height;
style.left = rect.left + (rect.width / 2);
break;
case 'n':
style.top = rect.top;
style.left = rect.left + (rect.width / 2);
break;
case 'e':
style.left = rect.left + rect.width;
style.top = rect.top + (rect.height / 2);
break;
case 'w':
style.left = rect.left;
style.top = rect.top + (rect.height / 2);
}
return <div>
<div className={ 'arr ' + o} style={style} />
<div className={ 'tip ' + o} style={style} ref={this._adjustPosition.bind(this)}>
{children}
return <div style={{ fontSize: this.context.sizeRatio + 'em' }}>
<div className={ 'arr ' + orientation} style={{ top, left: arrLeft }} />
<div className={ 'tip ' + orientation} style={{ top, left, width, height }} ref={(elem) => this.elem = elem}>
{this.props.children}
</div>
</div>;
}

View File

@@ -1,7 +1,8 @@
import React from 'react';
import cn from 'classnames';
import SlotSection from './SlotSection';
import HardpointSlot from './HardpointSlot';
import cn from 'classnames';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
/**
* Utility Slot Section
@@ -90,21 +91,21 @@ export default class UtilitySlotSection extends SlotSection {
_getSectionMenu(translate) {
let _use = this._use;
return <div className='select' onClick={(e) => e.stopPropagation()}>
return <div className='select' onTouchTap={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
<li className='lc' onTouchTap={this._empty}>{translate('empty all')}</li>
</ul>
<div className='select-group cap'>{translate('sb')}</div>
<ul>
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
<li className='c' onTouchTap={_use.bind(this, 'sb', 'E', null)}>E</li>
<li className='c' onTouchTap={_use.bind(this, 'sb', 'D', null)}>D</li>
<li className='c' onTouchTap={_use.bind(this, 'sb', 'C', null)}>C</li>
<li className='c' onTouchTap={_use.bind(this, 'sb', 'B', null)}>B</li>
<li className='c' onTouchTap={_use.bind(this, 'sb', 'A', null)}>A</li>
</ul>
<div className='select-group cap'>{translate('cm')}</div>
<ul>
<li className='lc' onClick={_use.bind(this, 'cm', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
<li className='lc' onTouchTap={_use.bind(this, 'cm', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
</ul>
</div>;
}