-
{translate(this.sectionName)}
+ this.sectionRefArr['ssHeadRef'] = ssHead}>{translate(this.sectionName)}
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
{this._getSlots()}
diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx
index 8e7f5075..6af5ab78 100644
--- a/src/app/components/StandardSlot.jsx
+++ b/src/app/components/StandardSlot.jsx
@@ -1,161 +1,159 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import cn from 'classnames';
-import Persist from '../stores/Persist';
-import TranslatedComponent from './TranslatedComponent';
-import { diffDetails } from '../utils/SlotFunctions';
-import AvailableModulesMenu from './AvailableModulesMenu';
-import ModificationsMenu from './ModificationsMenu';
-import * as ModuleUtils from '../shipyard/ModuleUtils';
-import { ListModifications, Modified } from './SvgIcons';
-import { Modifications } from 'coriolis-data/dist';
-import { stopCtxPropagation } from '../utils/UtilityFunctions';
-import { blueprintTooltip } from '../utils/BlueprintFunctions';
-
-/**
- * Standard Slot
- */
-export default class StandardSlot extends TranslatedComponent {
-
- static propTypes = {
- slot: PropTypes.object,
- modules: PropTypes.array.isRequired,
- onSelect: PropTypes.func.isRequired,
- onOpen: PropTypes.func.isRequired,
- onChange: PropTypes.func.isRequired,
- ship: PropTypes.object.isRequired,
- selected: PropTypes.bool,
- warning: PropTypes.func,
- };
-
- /**
- * Construct the slot
- * @param {object} props Object properties
- */
- constructor(props) {
- super(props);
- this._modificationsSelected = false;
- this._keyDown = this._keyDown.bind(this);
- this.modButton = null;
- this.slotDiv = null;
- }
-
- /**
- * Fired on key down
- * @param {KeyboardEvent} event The keydown event
- * @private
- */
- _keyDown(event) {
- if (event.key == 'Enter') {
- if(event.target.className == 'r') {
- this._toggleModifications();
- }
- this.props.onOpen(event);
- }
- }
-
- /**
- * Render the slot
- * @return {React.Component} Slot component
- */
- render() {
- let { termtip, tooltip } = this.context;
- let { translate, formats, units } = this.context.language;
- let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props;
- let m = slot.m;
- let classRating = m.class + m.rating;
- let menu;
- let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []);
- if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
- validMods = [];
- }
- let showModuleResistances = Persist.showModuleResistances();
- let mass = m.getMass() || m.cargo || m.fuel || 0;
-
- // Modifications tooltip shows blueprint and grade, if available
- let modTT = translate('modified');
- if (m && m.blueprint && m.blueprint.name) {
- modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
- if (m.blueprint.special && m.blueprint.special.id >= 0) {
- modTT += ', ' + translate(m.blueprint.special.name);
- }
- modTT = (
-
-
{modTT}
- {blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)}
-
- );
- }
-
- if (!selected) {
- // If not selected then sure that modifications flag is unset
- this._modificationsSelected = false;
- }
-
- const modificationsMarker = JSON.stringify(m);
-
- if (selected) {
- if (this._modificationsSelected) {
- menu =
;
- } else {
- menu =
;
- }
- }
-
- return (
-
this.slotDiv = slotDiv }>
-
-
{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}
-
-
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? : null }
-
{formats.round(mass)}{units.T}
-
-
- { m.getMinMass() ?
{translate('minimum mass')}: {formats.int(m.getMinMass())}{units.T}
: null }
- { m.getOptMass() ?
{translate('optimal mass')}: {formats.int(m.getOptMass())}{units.T}
: null }
- { m.getMaxMass() ?
{translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}
: null }
- { m.getOptMul() ?
{translate('optimal multiplier')}: {formats.rPct(m.getOptMul())}
: null }
- { m.getRange() ?
{translate('range', m.grp)}: {formats.f2(m.getRange())}{units.km}
: null }
- { m.time ?
{translate('time')}: {formats.time(m.time)}
: null }
- { m.getThermalEfficiency() ?
{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}
: null }
- { m.getPowerGeneration() > 0 ?
{translate('pgen')}: {formats.f1(m.getPowerGeneration())}{units.MW}
: null }
- { m.getMaxFuelPerJump() ?
{translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}
: null }
- { m.getWeaponsCapacity() ?
{translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}
: null }
- { m.getSystemsCapacity() ?
{translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}
: null }
- { m.getEnginesCapacity() ?
{translate('ENG')}: {formats.f1(m.getEnginesCapacity())}{units.MJ} / {formats.f1(m.getEnginesRechargeRate())}{units.MW}
: null }
- { showModuleResistances && m.getExplosiveResistance() ?
{translate('explres')}: {formats.pct(m.getExplosiveResistance())}
: null }
- { showModuleResistances && m.getKineticResistance() ?
{translate('kinres')}: {formats.pct(m.getKineticResistance())}
: null }
- { showModuleResistances && m.getThermalResistance() ?
{translate('thermres')}: {formats.pct(m.getThermalResistance())}
: null }
- { m.getIntegrity() ?
{translate('integrity')}: {formats.int(m.getIntegrity())}
: null }
- { validMods.length > 0 ?
this.modButton = modButton }>
: null }
-
-
-
- {menu}
-
- );
- }
-
- /**
- * Toggle the modifications flag when selecting the modifications icon
- */
- _toggleModifications() {
- this._modificationsSelected = !this._modificationsSelected;
- }
-}
+import React from 'react';
+import PropTypes from 'prop-types';
+import cn from 'classnames';
+import Persist from '../stores/Persist';
+import TranslatedComponent from './TranslatedComponent';
+import { diffDetails } from '../utils/SlotFunctions';
+import AvailableModulesMenu from './AvailableModulesMenu';
+import ModificationsMenu from './ModificationsMenu';
+import * as ModuleUtils from '../shipyard/ModuleUtils';
+import { ListModifications, Modified } from './SvgIcons';
+import { Modifications } from 'coriolis-data/dist';
+import { stopCtxPropagation } from '../utils/UtilityFunctions';
+import { blueprintTooltip } from '../utils/BlueprintFunctions';
+
+/**
+ * Standard Slot
+ */
+export default class StandardSlot extends TranslatedComponent {
+
+ static propTypes = {
+ slot: PropTypes.object,
+ modules: PropTypes.array.isRequired,
+ onSelect: PropTypes.func.isRequired,
+ onOpen: PropTypes.func.isRequired,
+ onChange: PropTypes.func.isRequired,
+ ship: PropTypes.object.isRequired,
+ selected: PropTypes.bool,
+ warning: PropTypes.func,
+ };
+
+ /**
+ * Construct the slot
+ * @param {object} props Object properties
+ */
+ constructor(props) {
+ super(props);
+ this._modificationsSelected = false;
+ this._keyDown = this._keyDown.bind(this);
+ this.modButton = null;
+ this.slotDiv = null;
+ }
+ /**
+ * Handle Enter key
+ * @param {SyntheticEvent} event KeyDown event
+ */
+ _keyDown(event) {
+ if (event.key == 'Enter') {
+ if(event.target.className == 'r') {
+ this._toggleModifications();
+ }
+ this.props.onOpen(event);
+ }
+ }
+
+ /**
+ * Render the slot
+ * @return {React.Component} Slot component
+ */
+ render() {
+ let { termtip, tooltip } = this.context;
+ let { translate, formats, units } = this.context.language;
+ let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props;
+ let m = slot.m;
+ let classRating = m.class + m.rating;
+ let menu;
+ let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []);
+ if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
+ validMods = [];
+ }
+ let showModuleResistances = Persist.showModuleResistances();
+ let mass = m.getMass() || m.cargo || m.fuel || 0;
+
+ // Modifications tooltip shows blueprint and grade, if available
+ let modTT = translate('modified');
+ if (m && m.blueprint && m.blueprint.name) {
+ modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
+ if (m.blueprint.special && m.blueprint.special.id >= 0) {
+ modTT += ', ' + translate(m.blueprint.special.name);
+ }
+ modTT = (
+
+
{modTT}
+ {blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)}
+
+ );
+ }
+
+ if (!selected) {
+ // If not selected then sure that modifications flag is unset
+ this._modificationsSelected = false;
+ }
+
+ const modificationsMarker = JSON.stringify(m);
+
+ if (selected) {
+ if (this._modificationsSelected) {
+ menu =
;
+ } else {
+ menu =
;
+ }
+ }
+
+ return (
+
this.slotDiv = slotDiv }>
+
+
{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}
+
+
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? : null }
+
{formats.round(mass)}{units.T}
+
+
+ { m.getMinMass() ?
{translate('minimum mass')}: {formats.int(m.getMinMass())}{units.T}
: null }
+ { m.getOptMass() ?
{translate('optimal mass')}: {formats.int(m.getOptMass())}{units.T}
: null }
+ { m.getMaxMass() ?
{translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}
: null }
+ { m.getOptMul() ?
{translate('optimal multiplier')}: {formats.rPct(m.getOptMul())}
: null }
+ { m.getRange() ?
{translate('range', m.grp)}: {formats.f2(m.getRange())}{units.km}
: null }
+ { m.time ?
{translate('time')}: {formats.time(m.time)}
: null }
+ { m.getThermalEfficiency() ?
{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}
: null }
+ { m.getPowerGeneration() > 0 ?
{translate('pgen')}: {formats.f1(m.getPowerGeneration())}{units.MW}
: null }
+ { m.getMaxFuelPerJump() ?
{translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}
: null }
+ { m.getWeaponsCapacity() ?
{translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}
: null }
+ { m.getSystemsCapacity() ?
{translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}
: null }
+ { m.getEnginesCapacity() ?
{translate('ENG')}: {formats.f1(m.getEnginesCapacity())}{units.MJ} / {formats.f1(m.getEnginesRechargeRate())}{units.MW}
: null }
+ { showModuleResistances && m.getExplosiveResistance() ?
{translate('explres')}: {formats.pct(m.getExplosiveResistance())}
: null }
+ { showModuleResistances && m.getKineticResistance() ?
{translate('kinres')}: {formats.pct(m.getKineticResistance())}
: null }
+ { showModuleResistances && m.getThermalResistance() ?
{translate('thermres')}: {formats.pct(m.getThermalResistance())}
: null }
+ { m.getIntegrity() ?
{translate('integrity')}: {formats.int(m.getIntegrity())}
: null }
+ { validMods.length > 0 ?
this.modButton = modButton }>
: null }
+
+
+
+ {menu}
+
+ );
+ }
+
+ /**
+ * Toggle the modifications flag when selecting the modifications icon
+ */
+ _toggleModifications() {
+ this._modificationsSelected = !this._modificationsSelected;
+ }
+}
diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx
index 566478f2..7ed5cb55 100644
--- a/src/app/components/StandardSlotSection.jsx
+++ b/src/app/components/StandardSlotSection.jsx
@@ -20,12 +20,23 @@ export default class StandardSlotSection extends SlotSection {
super(props, context, 'standard', 'core internal');
this._optimizeStandard = this._optimizeStandard.bind(this);
this._selectBulkhead = this._selectBulkhead.bind(this);
+ this.selectedRefId = null;
+ this.firstRefId = 'maxjump';
+ this.lastRefId = 'racer';
+ }
+ /**
+ * Handle focus if the component updates
+ * @param {Object} prevProps React Component properties
+ */
+ componentDidUpdate(prevProps) {
+ this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
}
/**
* Use the lightest/optimal available standard modules
*/
_optimizeStandard() {
+ this.selectedRefId = 'maxjump';
this.props.ship.useLightestStandard();
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -39,6 +50,8 @@ export default class StandardSlotSection extends SlotSection {
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
*/
_multiPurpose(shielded, bulkheadIndex) {
+ this.selectedRefId = 'multipurpose';
+ if (bulkheadIndex === 2) this.selectedRefId = 'combat';
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -51,6 +64,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} shielded True if shield generator should be included
*/
_optimizeCargo(shielded) {
+ this.selectedRefId = 'trader';
ShipRoles.trader(this.props.ship, shielded);
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -63,6 +77,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} shielded True if shield generator should be included
*/
_optimizeMiner(shielded) {
+ this.selectedRefId = 'miner';
ShipRoles.miner(this.props.ship, shielded);
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -75,6 +90,8 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
*/
_optimizeExplorer(planetary) {
+ this.selectedRefId = 'explorer';
+ if (planetary) this.selectedRefId = 'planetary';
ShipRoles.explorer(this.props.ship, planetary);
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -86,6 +103,7 @@ export default class StandardSlotSection extends SlotSection {
* Racer role
*/
_optimizeRacer() {
+ this.selectedRefId = 'racer';
ShipRoles.racer(this.props.ship);
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -229,17 +247,17 @@ export default class StandardSlotSection extends SlotSection {
let planetaryDisabled = this.props.ship.internal.length < 4;
return
e.stopPropagation()} onContextMenu={stopCtxPropagation}>
- {translate('Maximize Jump Range')}
+ this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}
{translate('roles')}
- {translate('Multi-purpose')}
- {translate('Combat')}
- {translate('Trader')}
- {translate('Explorer')}
- {translate('Planetary Explorer')}
- {translate('Miner')}
- {translate('Racer')}
+ this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}
+ this.sectionRefArr['combat'] = smRef}>{translate('Combat')}
+ this.sectionRefArr['trader'] = smRef}>{translate('Trader')}
+ this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}
+ this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}
+ this.sectionRefArr['miner'] = smRef}>{translate('Miner')}
+ this.sectionRefArr['racer'] = smRef}>{translate('Racer')}
;
}
diff --git a/src/app/components/UtilitySlotSection.jsx b/src/app/components/UtilitySlotSection.jsx
index 594e75d7..0b30e57b 100644
--- a/src/app/components/UtilitySlotSection.jsx
+++ b/src/app/components/UtilitySlotSection.jsx
@@ -17,6 +17,16 @@ export default class UtilitySlotSection extends SlotSection {
constructor(props, context) {
super(props, context, 'utility', 'utility mounts');
this._empty = this._empty.bind(this);
+ this.selectedRefId = null;
+ this.firstRefId = 'emptyall';
+ this.lastRefId = 'po';
+ }
+ /**
+ * Handle focus if the component updates
+ * @param {Object} prevProps React Component properties
+ */
+ componentDidUpdate(prevProps) {
+ this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
}
/**
@@ -36,6 +46,9 @@ export default class UtilitySlotSection extends SlotSection {
* @param {Synthetic} event Event
*/
_use(group, rating, name, event) {
+ this.selectedRefId = group;
+ if (rating !== null) this.selectedRefId += '-' + rating;
+
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
this.props.onChange();
this._close();
@@ -94,28 +107,28 @@ export default class UtilitySlotSection extends SlotSection {
return
e.stopPropagation()} onContextMenu={stopCtxPropagation}>
- {translate('empty all')}
+ this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}
{translate('PHRASE_ALT_ALL')}
{translate('sb')}
- A
- B
- C
- D
- E
+ this.sectionRefArr['sb-A'] = smRef}>A
+ this.sectionRefArr['sb-B'] = smRef}>B
+ this.sectionRefArr['sb-C'] = smRef}>C
+ this.sectionRefArr['sb-D'] = smRef}>D
+ this.sectionRefArr['sb-E'] = smRef}>E
{translate('hs')}
- {translate('Heat Sink Launcher')}
+ this.sectionRefArr['hs'] = smRef}>{translate('Heat Sink Launcher')}
{translate('ch')}
- {translate('Chaff Launcher')}
+ this.sectionRefArr['ch'] = smRef}>{translate('Chaff Launcher')}
{translate('po')}
- {translate('Point Defence')}
+ this.sectionRefArr['po'] = smRef}>{translate('Point Defence')}
;
}
diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx
index a26efcc4..81209df1 100644
--- a/src/app/pages/OutfittingPage.jsx
+++ b/src/app/pages/OutfittingPage.jsx
@@ -58,6 +58,7 @@ export default class OutfittingPage extends Page {
this._fuelUpdated = this._fuelUpdated.bind(this);
this._opponentUpdated = this._opponentUpdated.bind(this);
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
+ this._sectionMenuRefs = {};
}
/**
@@ -555,7 +556,6 @@ export default class OutfittingPage extends Page {
const requirements = Ships[ship.id].requirements;
let requirementElements = [];
-
/**
* Render the requirements for a ship / etc
* @param {string} className Class names
@@ -608,11 +608,11 @@ export default class OutfittingPage extends Page {
{/* Main tables */}
-