From 6f67267fecfc52913926bccead88e25483165a85 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 10 Mar 2017 17:17:37 +0000 Subject: [PATCH] Building components for battle centre --- src/app/components/BattleCentre.jsx | 98 +++++++ src/app/components/Cargo.jsx | 102 +++++++ src/app/components/EngagementRange.jsx | 123 +++++++++ src/app/components/Fuel.jsx | 96 +++++++ src/app/components/Pips.jsx | 358 +++++++++++++++++++++++++ src/app/components/SvgIcons.jsx | 18 ++ src/app/i18n/Language.jsx | 22 +- src/app/pages/OutfittingPage.jsx | 10 +- src/less/app.less | 1 + src/less/outfit.less | 8 + src/less/pips.less | 29 ++ 11 files changed, 846 insertions(+), 19 deletions(-) create mode 100644 src/app/components/BattleCentre.jsx create mode 100644 src/app/components/Cargo.jsx create mode 100644 src/app/components/EngagementRange.jsx create mode 100644 src/app/components/Fuel.jsx create mode 100644 src/app/components/Pips.jsx create mode 100755 src/less/pips.less diff --git a/src/app/components/BattleCentre.jsx b/src/app/components/BattleCentre.jsx new file mode 100644 index 00000000..fc6297ea --- /dev/null +++ b/src/app/components/BattleCentre.jsx @@ -0,0 +1,98 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import { Ships } from 'coriolis-data/dist'; +import Slider from '../components/Slider'; +import Pips from '../components/Pips'; +import Fuel from '../components/Fuel'; +import Cargo from '../components/Cargo'; +import EngagementRange from '../components/EngagementRange'; + +/** + * Battle centre allows you to pit your current build against another ship, + * adjust pips and engagement range, and see a wide variety of information + */ +export default class BattleCentre extends TranslatedComponent { + static PropTypes = { + ship: React.PropTypes.object.isRequired + }; + + static DEFAULT_OPPONENT = { ship: Ships['anaconda'] }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + + const { ship } = this.props; + const opponent = BattleCentre.DEFAULT_OPPONENT; + + this.state = { }; + } + + componentWillReceiveProps(nextProps) { + // Rather than try to keep track of what changes our children require we force an update and let them work it out + this.forceUpdate(); + return true; + } + + /** + * Triggered when pips have been updated + */ + _pipsUpdated(sys, eng, wep) { + console.log('Pips are now ' + sys + '/' + eng + '/' + wep); + } + + /** + * Triggered when fuel has been updated + */ + _fuelUpdated(fuel) { + console.log('Fuel is now ' + fuel); + } + + /** + * Triggered when cargo has been updated + */ + _cargoUpdated(cargo) { + console.log('Cargo is now ' + cargo); + } + + /** + * Triggered when engagement range has been updated + */ + _engagementRangeUpdated(engagementRange) { + console.log('Engagement range is now ' + engagementRange); + } + + /** + * Render + * @return {React.Component} contents + */ + render() { + const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; + const { formats, translate, units } = language; + const { against, expanded, maxRange, range, totals } = this.state; + const { ship } = this.props; + const shipUpdated = this._shipUpdated; + const pipsUpdated = this._pipsUpdated; + const fuelUpdated = this._fuelUpdated; + const cargoUpdated = this._cargoUpdated; + const engagementRangeUpdated = this._engagementRangeUpdated; + + return ( + +

{translate('battle centre')}

+
+ +
+
+ + + +
+
+ ); + } +} diff --git a/src/app/components/Cargo.jsx b/src/app/components/Cargo.jsx new file mode 100644 index 00000000..1e7ddbaf --- /dev/null +++ b/src/app/components/Cargo.jsx @@ -0,0 +1,102 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import { Ships } from 'coriolis-data/dist'; +import Slider from '../components/Slider'; + +/** + * Cargo slider + * Requires an onChange() function of the form onChange(cargo), providing the cargo in tonnes, which is triggered on cargo level change + */ +export default class Cargo extends TranslatedComponent { + static PropTypes = { + ship: React.PropTypes.object.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + + const ship = this.props.ship; + + this.state = { + cargoCapacity: ship.cargoCapacity, + cargoLevel: 1, + }; + } + + /** + * Update the state if our ship changes + * @param {Object} nextProps Incoming/Next properties + * @return {boolean} Returns true if the component should be rerendered + */ + componentWillReceiveProps(nextProps) { + const { cargoLevel, cargoCapacity } = this.state; + const nextCargoCapacity = nextProps.ship.cargoCapacity; + + if (nextCargoCapacity != cargoCapacity) { + // We keep the absolute cargo amount the same if possible so recalculate the relative level + const nextCargoLevel = Math.min((cargoLevel * cargoCapacity) / nextCargoCapacity, 1); + + this.setState({ cargoLevel: nextCargoLevel, cargoCapacity: nextCargoCapacity }); + + // Notify if appropriate + if (nextCargoLevel * nextCargoCapacity != cargoLevel * cargoCapacity) { + this.props.onChange(Math.round(nextCargoLevel * nextCargoCapacity)); + } + } + return true; + } + + /** + * Update cargo level + * @param {number} cargoLevel percentage level from 0 to 1 + */ + _cargoChange(cargoLevel) { + const { cargoCapacity } = this.state; + // We round the cargo level to a suitable value given the capacity + cargoLevel = Math.round(cargoLevel * cargoCapacity) / cargoCapacity; + + if (cargoLevel != this.state.cargoLevel) { + this.setState({ cargoLevel }); + this.props.onChange(Math.round(cargoLevel * cargoCapacity)); + } + } + + /** + * Render cargo slider + * @return {React.Component} contents + */ + render() { + const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; + const { formats, translate, units } = language; + const { cargoLevel, cargoCapacity } = this.state; + + return ( + +

{translate('cargo carried')}: {formats.int(cargoLevel * cargoCapacity)}{units.T}

+ + + + + + +
+ +
+
+ ); + } +} diff --git a/src/app/components/EngagementRange.jsx b/src/app/components/EngagementRange.jsx new file mode 100644 index 00000000..adfee2cd --- /dev/null +++ b/src/app/components/EngagementRange.jsx @@ -0,0 +1,123 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import { Ships } from 'coriolis-data/dist'; +import Slider from '../components/Slider'; + +/** + * Engagement range slider + * Requires an onChange() function of the form onChange(range), providing the range in metres, which is triggered on range change + */ +export default class Range extends TranslatedComponent { + static PropTypes = { + ship: React.PropTypes.object.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + + const ship = this.props.ship; + + const maxRange = this._calcMaxRange(ship); + + this.state = { + maxRange: maxRange, + rangeLevel: 1, + }; + } + + /** + * Update the state if our ship changes + * @param {Object} nextProps Incoming/Next properties + * @return {boolean} Returns true if the component should be rerendered + */ + componentWillReceiveProps(nextProps) { + const { rangeLevel, maxRange } = this.state; + const nextMaxRange = this._calcMaxRange(nextProps.ship); + + if (nextMaxRange != maxRange) { + // We keep the absolute range amount the same if possible so recalculate the relative level + const nextRangeLevel = Math.min((rangeLevel * maxRange) / nextMaxRange, 1); + + this.setState({ rangeLevel: nextRangeLevel, maxRange: nextMaxRange }); + + // Notify if appropriate + if (nextRangeLevel * nextMaxRange != rangeLevel * maxRange) { + this.props.onChange(Math.round(nextRangeLevel * nextMaxRange)); + } + } + return true; + } + + /** + * Calculate the maximum range of a ship's weapons + * @param {Object} ship The ship + * @returns {int} The maximum range, in metres + */ + _calcMaxRange(ship) { + let maxRange = 1000; + for (let i = 0; i < ship.hardpoints.length; i++) { + if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) { + const thisRange = ship.hardpoints[i].m.getRange(); + if (thisRange > maxRange) { + maxRange = thisRange; + } + } + } + + return maxRange; + } + + /** + * Update range + * @param {number} range percentage level from 0 to 1 + */ + _rangeChange(rangeLevel) { + const { maxRange } = this.state; + // We round the range to an integer value + rangeLevel = Math.round(rangeLevel * maxRange) / maxRange; + + if (rangeLevel != this.state.rangeLevel) { + this.setState({ rangeLevel }); + this.props.onChange(Math.round(rangeLevel * maxRange)); + } + } + + /** + * Render range slider + * @return {React.Component} contents + */ + render() { + const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; + const { formats, translate, units } = language; + const { rangeLevel, maxRange } = this.state; + + return ( + +

{translate('engagement range')}: {formats.int(rangeLevel * maxRange)}{translate('m')}

+ + + + + + +
+ +
+
+ ); + } +} diff --git a/src/app/components/Fuel.jsx b/src/app/components/Fuel.jsx new file mode 100644 index 00000000..eb5d98c6 --- /dev/null +++ b/src/app/components/Fuel.jsx @@ -0,0 +1,96 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import { Ships } from 'coriolis-data/dist'; +import Slider from '../components/Slider'; + +/** + * Fuel slider + * Requires an onChange() function of the form onChange(fuel), providing the fuel in tonnes, which is triggered on fuel level change + */ +export default class Fuel extends TranslatedComponent { + static PropTypes = { + ship: React.PropTypes.object.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + + const ship = this.props.ship; + + this.state = { + fuelCapacity: ship.fuelCapacity, + fuelLevel: 1, + }; + } + + /** + * Update the state if our ship changes + * @param {Object} nextProps Incoming/Next properties + * @return {boolean} Returns true if the component should be rerendered + */ + componentWillReceiveProps(nextProps) { + const { fuelLevel, fuelCapacity } = this.state; + const nextFuelCapacity = nextProps.ship.fuelCapacity; + + if (nextFuelCapacity != fuelCapacity) { + // We keep the absolute fuel amount the same if possible so recalculate the relative level + const nextFuelLevel = Math.min((fuelLevel * fuelCapacity) / nextFuelCapacity, 1); + + this.setState({ fuelLevel: nextFuelLevel, fuelCapacity: nextFuelCapacity }); + + // Notify if appropriate + if (nextFuelLevel * nextFuelCapacity != fuelLevel * fuelCapacity) { + this.props.onChange(nextFuelLevel * nextFuelCapacity); + } + } + return true; + } + + /** + * Update fuel level + * @param {number} fuelLevel percentage level from 0 to 1 + */ + _fuelChange(fuelLevel) { + this.setState({ fuelLevel }); + this.props.onChange(fuelLevel * this.state.fuelCapacity); + } + + /** + * Render fuel slider + * @return {React.Component} contents + */ + render() { + const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; + const { formats, translate, units } = language; + const { fuelLevel, fuelCapacity } = this.state; + + return ( + +

{translate('fuel carried')}: {formats.f2(fuelLevel * fuelCapacity)}{units.T}

+ + + + + + +
+ +
+
+ ); + } +} diff --git a/src/app/components/Pips.jsx b/src/app/components/Pips.jsx new file mode 100644 index 00000000..b6ecd59a --- /dev/null +++ b/src/app/components/Pips.jsx @@ -0,0 +1,358 @@ +import React from 'react'; +import TranslatedComponent from './TranslatedComponent'; +import { Ships } from 'coriolis-data/dist'; +import ShipSelector from './ShipSelector'; +import { nameComparator } from '../utils/SlotFunctions'; +import { Pip } from './SvgIcons'; +import LineChart from '../components/LineChart'; +import Slider from '../components/Slider'; +import * as ModuleUtils from '../shipyard/ModuleUtils'; +import Module from '../shipyard/Module'; + +/** + * Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area. + * Requires an onChange() function of the form onChange(sys, eng, wep) which is triggered whenever the pips change. + */ +export default class Pips extends TranslatedComponent { + static PropTypes = { + ship: React.PropTypes.object.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + const ship = props.ship; + const pd = ship.standard[4].m; + + this._keyDown = this._keyDown.bind(this); + + let pipsSvg = this._renderPips(2, 2, 2); + this.state = { + sys: 2, + eng: 2, + wep: 2, + sysCap: pd.getSystemsCapacity(), + engCap: pd.getEnginesCapacity(), + wepCap: pd.getWeaponsCapacity(), + sysRate: pd.getSystemsRechargeRate(), + engRate: pd.getEnginesRechargeRate(), + wepRate: pd.getWeaponsRechargeRate(), + pipsSvg + }; + } + + /** + * Add listeners after mounting + */ + componentDidMount() { + document.addEventListener('keydown', this._keyDown); + } + + /** + * Remove listeners before unmounting + */ + componentWillUnmount() { + document.removeEventListener('keydown', this._keyDown); + } + + /** + * Update values if we change ship + * @param {Object} nextProps Incoming/Next properties + * @returns {boolean} Returns true if the component should be rerendered + */ + componentWillReceiveProps(nextProps) { + const { sysCap, engCap, wepCap, sysRate, engRate, wepRate } = this.state; + const ship = nextProps.ship; + const pd = ship.standard[4].m; + + const nextSysCap = pd.getSystemsCapacity(); + const nextEngCap = pd.getEnginesCapacity(); + const nextWepCap = pd.getWeaponsCapacity(); + const nextSysRate = pd.getSystemsRechargeRate(); + const nextEngRate = pd.getEnginesRechargeRate(); + const nextWepRate = pd.getWeaponsRechargeRate(); + if (nextSysCap != sysCap || + nextEngCap != engCap || + nextWepCap != wepCap || + nextSysRate != sysRate || + nextEngRate != engRate || + nextWepRate != wepRate) { + this.setState({ + sysCap: nextSysCap, + engCap: nextEngCap, + wepCap: nextWepCap, + sysRate: nextSysRate, + engRate: nextEngRate, + wepRate: nextWepRate + }); + } + + return true; + } + + /** + * Handle Key Down + * @param {Event} e Keyboard Event + */ + _keyDown(e) { + switch (e.keyCode) { + case 37: // Left arrow == increase SYS + e.preventDefault(); + this._incSys(); + break; + case 38: // Up arrow == increase ENG + e.preventDefault(); + this._incEng(); + break; + case 39: // Right arrow == increase WEP + e.preventDefault(); + this._incWep(); + break; + case 40: // Down arrow == reset + e.preventDefault(); + this._reset(); + break; + } + } + + /** + * Reset the capacitor + */ + _reset() { + let { sys, eng, wep } = this.state; + if (sys != 2 || eng != 2 || wep != 2) { + sys = eng = wep = 2; + this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) }); + this.props.onChange(sys, eng, wep); + } + } + + /** + * Increment the SYS capacitor + */ + _incSys() { + let { sys, eng, wep } = this.state; + + const required = Math.min(1, 4 - sys); + if (required > 0) { + if (required == 0.5) { + // Take from whichever is larger + if (eng > wep) { + eng -= 0.5; + sys += 0.5; + } else { + wep -= 0.5; + sys += 0.5; + } + } else { + // Required is 1 - take from both if possible + if (eng == 0) { + wep -= 1; + sys += 1; + } else if (wep == 0) { + eng -= 1; + sys += 1; + } else { + eng -= 0.5; + wep -= 0.5; + sys += 1; + } + } + this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) }); + this.props.onChange(sys, eng, wep); + } + } + + /** + * Increment the ENG capacitor + */ + _incEng() { + let { sys, eng, wep } = this.state; + + const required = Math.min(1, 4 - eng); + if (required > 0) { + if (required == 0.5) { + // Take from whichever is larger + if (sys > wep) { + sys -= 0.5; + eng += 0.5; + } else { + wep -= 0.5; + eng += 0.5; + } + } else { + // Required is 1 - take from both if possible + if (sys == 0) { + wep -= 1; + eng += 1; + } else if (wep == 0) { + sys -= 1; + eng += 1; + } else { + sys -= 0.5; + wep -= 0.5; + eng += 1; + } + } + this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) }); + this.props.onChange(sys, eng, wep); + } + } + + /** + * Increment the WEP capacitor + */ + _incWep() { + let { sys, eng, wep } = this.state; + + const required = Math.min(1, 4 - wep); + if (required > 0) { + if (required == 0.5) { + // Take from whichever is larger + if (sys > eng) { + sys -= 0.5; + wep += 0.5; + } else { + eng -= 0.5; + wep += 0.5; + } + } else { + // Required is 1 - take from both if possible + if (sys == 0) { + eng -= 1; + wep += 1; + } else if (eng == 0) { + sys -= 1; + wep += 1; + } else { + sys -= 0.5; + eng -= 0.5; + wep += 1; + } + } + this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) }); + this.props.onChange(sys, eng, wep); + } + } + + /** + * Handle a click + */ + onClick(which) { + if (which == 'SYS') { + this._incSys(); + } else if (which == 'ENG') { + this._incEng(); + } else if (which == 'WEP') { + this._incWep(); + } else if (which == 'RST') { + this._reset(); + } + } + + /** + * Set up the rendering for pips + * @param {int} sys the SYS pips + * @param {int} eng the ENG pips + * @param {int} wep the WEP pips + * @returns {Object} Object containing the rendering for the pips + */ + _renderPips(sys, eng, wep) { + const pipsSvg = {}; + + // SYS + pipsSvg['SYS'] = []; + for (let i = 0; i < Math.floor(sys); i++) { + pipsSvg['SYS'].push(); + } + if (sys > Math.floor(sys)) { + pipsSvg['SYS'].push(); + } + for (let i = Math.floor(sys + 0.5); i < 4; i++) { + pipsSvg['SYS'].push(); + } + + // ENG + pipsSvg['ENG'] = []; + for (let i = 0; i < Math.floor(eng); i++) { + pipsSvg['ENG'].push(); + } + if (eng > Math.floor(eng)) { + pipsSvg['ENG'].push(); + } + for (let i = Math.floor(eng + 0.5); i < 4; i++) { + pipsSvg['ENG'].push(); + } + + // WEP + pipsSvg['WEP'] = []; + for (let i = 0; i < Math.floor(wep); i++) { + pipsSvg['WEP'].push(); + } + if (wep > Math.floor(wep)) { + pipsSvg['WEP'].push(); + } + for (let i = Math.floor(wep + 0.5); i < 4; i++) { + pipsSvg['WEP'].push(); + } + + return pipsSvg; + } + + /** + * Render pips + * @return {React.Component} contents + */ + render() { + const { formats, translate, units } = this.context.language; + const { ship } = this.props; + const { sys, eng, wep, sysCap, engCap, wepCap, sysRate, engRate, wepRate, pipsSvg } = this.state; + + const onSysClicked = this.onClick.bind(this, 'SYS'); + const onEngClicked = this.onClick.bind(this, 'ENG'); + const onWepClicked = this.onClick.bind(this, 'WEP'); + const onRstClicked = this.onClick.bind(this, 'RST'); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  {pipsSvg['ENG']} 
 {pipsSvg['SYS']}{translate('ENG')}{pipsSvg['WEP']}
 {translate('SYS')}{translate('RST')}{translate('WEP')}
{translate('capacity')} ({units.MJ}){formats.f1(sysCap)}{formats.f1(engCap)}{formats.f1(wepCap)}
{translate('recharge')} ({units.MW}){formats.f1(sysRate * (sys / 4))}{formats.f1(engRate * (eng / 4))}{formats.f1(wepRate * (wep / 4))}
+ ); + } +} diff --git a/src/app/components/SvgIcons.jsx b/src/app/components/SvgIcons.jsx index af119679..4593f173 100644 --- a/src/app/components/SvgIcons.jsx +++ b/src/app/components/SvgIcons.jsx @@ -708,6 +708,24 @@ export class Switch extends SvgIcon { } } +/** + * Pip + */ +export class Pip extends SvgIcon { + /** + * Overriden view box + * @return {String} view box + */ + viewBox() { return '0 0 200 200'; } + /** + * Generate the SVG + * @return {React.Component} SVG Contents + */ + svg() { + return ; + } +} + /** * In-game Coriolis Station logo */ diff --git a/src/app/i18n/Language.jsx b/src/app/i18n/Language.jsx index f22c0d42..e8d81da0 100644 --- a/src/app/i18n/Language.jsx +++ b/src/app/i18n/Language.jsx @@ -58,21 +58,21 @@ export function getLanguage(langCode) { }, translate, units: { - CR: {translate('CR')}, // Credits - kg: {translate('kg')}, // Kilograms - kgs: {translate('kg/s')}, // Kilograms per second - km: {translate('km')}, // Kilometers - Ls: {translate('Ls')}, // Light Seconds - LY: {translate('LY')}, // Light Years - MJ: {translate('MJ')}, // Mega Joules - 'm/s': {translate('m/s')}, // Meters per second - '°/s': {translate('°/s')}, // Degrees per second - MW: {translate('MW')}, // Mega Watts (same as Mega Joules per second) + CR: {translate('CR')}, // Credits + kg: {translate('kg')}, // Kilograms + kgs: {translate('kg/s')}, // Kilograms per second + km: {translate('km')}, // Kilometers + Ls: {translate('Ls')}, // Light Seconds + LY: {translate('LY')}, // Light Years + MJ: {translate('MJ')}, // Mega Joules + 'm/s': {translate('m/s')}, // Meters per second + '°/s': {translate('°/s')}, // Degrees per second + MW: {translate('MW')}, // Mega Watts (same as Mega Joules per second) mps: {translate('m/s')}, // Metres per second ps: {translate('/s')}, // per second pm: {translate('/min')}, // per minute s: {translate('secs')}, // Seconds - T: {translate('T')}, // Metric Tons + T: {translate('T')}, // Metric Tons } }; } diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx index 0e50fc63..fb0ffbd3 100644 --- a/src/app/pages/OutfittingPage.jsx +++ b/src/app/pages/OutfittingPage.jsx @@ -20,13 +20,11 @@ import MovementSummary from '../components/MovementSummary'; import EngineProfile from '../components/EngineProfile'; import FSDProfile from '../components/FSDProfile'; import JumpRange from '../components/JumpRange'; -import DamageDealt from '../components/DamageDealt'; -import DamageReceived from '../components/DamageReceived'; +import BattleCentre from '../components/BattleCentre'; import PowerManagement from '../components/PowerManagement'; import CostSection from '../components/CostSection'; import ModalExport from '../components/ModalExport'; import ModalPermalink from '../components/ModalPermalink'; -import Slider from '../components/Slider'; /** * Document Title Generator @@ -373,11 +371,7 @@ export default class OutfittingPage extends Page {
- -
- -
- +
diff --git a/src/less/app.less b/src/less/app.less index 24ade30c..303a3468 100755 --- a/src/less/app.less +++ b/src/less/app.less @@ -19,6 +19,7 @@ @import 'shipselector'; @import 'sortable'; @import 'loader'; +@import 'pips'; html, body { height: 100%; diff --git a/src/less/outfit.less b/src/less/outfit.less index 8d5e7f0e..7b792542 100755 --- a/src/less/outfit.less +++ b/src/less/outfit.less @@ -198,6 +198,14 @@ }); } + &.twothirds { + width: 67%; + + .smallTablet({ + width: 100% !important; + }); + } + .smallScreen({ .axis.x { g.tick:nth-child(2n + 1) text { diff --git a/src/less/pips.less b/src/less/pips.less new file mode 100755 index 00000000..d1b09f43 --- /dev/null +++ b/src/less/pips.less @@ -0,0 +1,29 @@ +// The pips table - keep the background black +.pipstable { + background-color: @bgBlack; + color: @primary; +} + +// A clickable entity in the pips table +.pipsclickable { + cursor: pointer; +} + +// A full pip +.fullpip { + stroke: @primary; + fill: @primary; +} + +// A half pip +.halfpip { + stroke: @primary-disabled; + fill: @primary-disabled; +} + +// An empty pip +.emptypip { + stroke: @primary-bg; + fill: @primary-bg; +} +