import React from 'react'; import PropTypes from 'prop-types'; import TranslatedComponent from './TranslatedComponent'; import { wrapCtxMenu } from '../utils/UtilityFunctions'; import { canMount } from '../utils/SlotFunctions'; import { Equalizer } from '../components/SvgIcons'; import cn from 'classnames'; const browser = require('detect-browser'); /** * Abstract Slot Section */ export default class SlotSection extends TranslatedComponent { static propTypes = { ship: PropTypes.object.isRequired, onChange: PropTypes.func.isRequired, onCargoChange: PropTypes.func.isRequired, onFuelChange: PropTypes.func.isRequired, code: PropTypes.string.isRequired, togglePwr: PropTypes.func }; /** * Constructor * @param {Object} props React Component properties * @param {Object} context React Component context * @param {string} sectionId Section DOM Id * @param {string} sectionName Section name */ constructor(props, context, sectionId, sectionName) { super(props); this.sectionId = sectionId; this.sectionName = sectionName; this._getSlots = this._getSlots.bind(this); this._selectModule = this._selectModule.bind(this); this._getSectionMenu = this._getSectionMenu.bind(this); this._contextMenu = this._contextMenu.bind(this); this._drop = this._drop.bind(this); this._dragOverNone = this._dragOverNone.bind(this); this._close = this._close.bind(this); this.state = {}; } // Must be implemented by subclasses: // _getSlots() // _getSectionMenu() // _contextMenu() /** * Open a menu * @param {string} menu Menu name * @param {SyntheticEvent} event Event */ _openMenu(menu, event) { event.preventDefault(); event.stopPropagation(); if (this.props.currentMenu === menu) { menu = null; } this.context.openMenu(menu); } /** * Mount/Use the specified module in the slot * @param {Object} slot Slot * @param {Object} m Selected module */ _selectModule(slot, m) { this.props.ship.use(slot, m, false); this.props.onChange(); this._close(); } /** * Slot Drag Handler * @param {object} originSlot Origin slot model * @param {Event} e Drag Event */ _drag(originSlot, e) { if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) { e.dataTransfer.setData('text/html', e.currentTarget); } e.dataTransfer.effectAllowed = 'copyMove'; this.setState({ originSlot, copy: e.getModifierState('Alt') }); this._close(); } /** * Slot Drag Over Handler * @param {object} targetSlot Potential drop target * @param {Event} e Drag Event */ _dragOverSlot(targetSlot, e) { e.preventDefault(); e.stopPropagation(); let os = this.state.originSlot; if (os) { // Show correct icon const effect = this.state.copy ? 'copy' : 'move'; if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) { e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? effect : 'none'; } this.setState({ targetSlot }); } else { if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) { e.dataTransfer.dropEffect = 'none'; } } } /** * Drag over non-droppable target/element * @param {Event} e Drag Event */ _dragOverNone(e) { e.preventDefault(); if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) { e.dataTransfer.dropEffect = 'none'; } this.setState({ targetSlot: null }); } /** * Slot drop handler. If the target is eligible swap the origin and target modules. * If the target slot's current module cannot be mounted in the origin slot then * the origin slot will be empty. */ _drop() { let { originSlot, targetSlot, copy } = this.state; let m = originSlot.m; if (targetSlot && originSlot != targetSlot) { if (copy) { // We want to copy the module in to the target slot if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) { const mCopy = m.clone(); this.props.ship.use(targetSlot, mCopy, false); // Copy power info targetSlot.enabled = originSlot.enabled; targetSlot.priority = originSlot.priority; this.props.onChange(); } } else { // Store power info const originEnabled = targetSlot.enabled; const originPriority = targetSlot.priority; const targetEnabled = originSlot.enabled; const targetPriority = originSlot.priority; // We want to move the module in to the target slot, and swap back any module that was originally in the target slot if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) { // Swap modules if possible if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) { this.props.ship.use(originSlot, targetSlot.m, true); this.props.ship.use(targetSlot, m); // Swap power originSlot.enabled = originEnabled; originSlot.priority = originPriority; targetSlot.enabled = targetEnabled; targetSlot.priority = targetPriority; } else { // Otherwise empty the origin slot // Store power const targetEnabled = originSlot.enabled; this.props.ship.use(originSlot, null, true); // Empty but prevent summary update this.props.ship.use(targetSlot, m); originSlot.enabled = 0; originSlot.priority = 0; targetSlot.enabled = targetEnabled; targetSlot.priority = targetPriority; } this.props.onChange(); } } } this.setState({ originSlot: null, targetSlot: null, copy: null }); } /** * Determine drop eligibilty CSS class * @param {Object} slot Current slot * @param {Object} originSlot Origin slot * @param {Object} targetSlot Target slot * @return {string} CSS Class name */ _dropClass(slot, originSlot, targetSlot) { if (!originSlot) { return null; } if (slot === originSlot) { if (targetSlot && targetSlot.m && !canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) { return 'dropEmpty'; // Origin slot will be emptied } return null; } if (originSlot.m && canMount(this.props.ship, slot, originSlot.m.grp, originSlot.m.class)) { // Eligble drop slot if (slot === targetSlot) { return 'drop'; // Can drop } return 'eligible'; // Potential drop slot } return 'ineligible'; // Cannot be dropped / invalid drop slot } /** * Close current menu */ _close() { if (this.props.currentMenu) { this.context.closeMenu(); } } /** * Render the slot section * @return {React.Component} Slot section */ render() { let translate = this.context.language.translate; let sectionMenuOpened = this.props.currentMenu === this.sectionName; let open = this._openMenu.bind(this, this.sectionName); let ctx = wrapCtxMenu(this._contextMenu); return (

{translate(this.sectionName)}

{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
{this._getSlots()}
); } }