This commit is contained in:
Artur
2020-01-20 13:42:10 +02:00
parent 1f718df41a
commit 782125b339
32 changed files with 2109 additions and 60 deletions
@@ -57,6 +57,13 @@ const mapColorPropTo = attrName => (style, value) => {
const styleMapperByPropName = {
// Direct mapping
position: mapRawPropTo('position'),
overflow: mapRawPropTo('overflow'),
overflowX: mapRawPropTo('overflow-x'),
overflowY: mapRawPropTo('overflow-y'),
top: mapUnitPropTo('top'),
bottom: mapUnitPropTo('bottom'),
left: mapUnitPropTo('left'),
right: mapUnitPropTo('right'),
width: mapUnitPropTo('width'),
minWidth: mapUnitPropTo('min-width'),
maxWidth: mapUnitPropTo('max-width'),
@@ -68,6 +75,7 @@ const styleMapperByPropName = {
lineHeight: mapUnitPropTo('line-height'),
opacity: mapRawPropTo('opacity'),
textAlign: mapRawPropTo('text-align'),
verticalAlign: mapRawPropTo('vertical-align'),
// Boolean props
inline: mapBooleanPropTo('display', 'inline-block'),
bold: mapBooleanPropTo('font-weight', 'bold'),
+152 -2
View File
@@ -1,11 +1,14 @@
import { classes, pureComponentHooks } from 'common/react';
import { tridentVersion } from '../byond';
import { tridentVersion, act } from '../byond';
import { KEY_ENTER, KEY_ESCAPE, KEY_SPACE } from '../hotkeys';
import { createLogger } from '../logging';
import { refocusLayout } from '../refocus';
import { Box } from './Box';
import { Icon } from './Icon';
import { Tooltip } from './Tooltip';
import { Input } from './Input';
import { Component, createRef } from 'inferno';
import { Grid } from './Grid';
const logger = createLogger('Button');
@@ -21,6 +24,8 @@ export const Button = props => {
tooltipPosition,
ellipsis,
content,
iconRotation,
iconSpin,
children,
onclick,
onClick,
@@ -78,7 +83,7 @@ export const Button = props => {
}}
{...rest}>
{icon && (
<Icon name={icon} />
<Icon name={icon} rotation={iconRotation} spin={iconSpin} />
)}
{content}
{children}
@@ -105,3 +110,148 @@ export const ButtonCheckbox = props => {
};
Button.Checkbox = ButtonCheckbox;
export class ButtonConfirm extends Component {
constructor() {
super();
this.state = {
clickedOnce: false,
};
this.handleClick = () => {
if (this.state.clickedOnce) {
this.setClickedOnce(false);
}
};
}
setClickedOnce(clickedOnce) {
this.setState({
clickedOnce,
});
if (clickedOnce) {
setTimeout(() => window.addEventListener('click', this.handleClick));
}
else {
window.removeEventListener('click', this.handleClick);
}
}
render() {
const {
confirmMessage = "Confirm?",
confirmColor = "bad",
color,
content,
onClick,
...rest
} = this.props;
return (
<Button
content={this.state.clickedOnce ? confirmMessage : content}
color={this.state.clickedOnce ? confirmColor : color}
onClick={() => this.state.clickedOnce
? onClick()
: this.setClickedOnce(true)}
{...rest}
/>
);
}
}
Button.Confirm = ButtonConfirm;
export class ButtonInput extends Component {
constructor() {
super();
this.inputRef = createRef();
this.state = {
inInput: false,
};
}
setInInput(inInput) {
this.setState({
inInput,
});
if (this.inputRef) {
const input = this.inputRef.current;
if (inInput) {
input.value = this.props.currentValue || "";
try {
input.focus();
input.select();
}
catch {}
}
}
}
commitResult(e) {
if (this.inputRef) {
const input = this.inputRef.current;
const hasValue = (input.value !== "");
if (hasValue) {
this.props.onCommit(e, input.value);
return;
} else {
if (!this.props.defaultValue) {
return;
}
this.props.onCommit(e, this.props.defaultValue);
}
}
}
render() {
const {
fluid,
content,
color = 'default',
placeholder,
maxLength,
...rest
} = this.props;
return (
<Box
className={classes([
'Button',
fluid && 'Button--fluid',
'Button--color--' + color,
])}
{...rest}
onClick={() => this.setInInput(true)}>
<div>
{content}
</div>
<input
ref={this.inputRef}
className="NumberInput__input"
style={{
'display': !this.state.inInput ? 'none' : undefined,
'text-align': 'left',
}}
onBlur={e => {
if (!this.state.inInput) {
return;
}
this.setInInput(false);
this.commitResult(e);
}}
onKeyDown={e => {
if (e.keyCode === KEY_ENTER) {
this.setInInput(false);
this.commitResult(e);
return;
}
if (e.keyCode === KEY_ESCAPE) {
this.setInInput(false);
}
}}
/>
</Box>
);
}
}
Button.Input = ButtonInput;
@@ -1,5 +1,5 @@
import { classes } from 'common/react';
import { Component } from 'inferno';
import { Component, createRef } from 'inferno';
import { Box } from './Box';
import { Icon } from './Icon';
@@ -25,6 +25,7 @@ export class Dropdown extends Component {
this.setState({ open: open });
if (open) {
setTimeout(() => window.addEventListener('click', this.handleClick));
this.menuRef.focus();
}
else {
window.removeEventListener('click', this.handleClick);
@@ -61,7 +62,6 @@ export class Dropdown extends Component {
over,
width,
onClick,
onSet,
selected,
...boxProps
} = props;
@@ -73,14 +73,18 @@ export class Dropdown extends Component {
const adjustedOpen = over ? !this.state.open : this.state.open;
const menu = this.state.open ? (
<Box
width={width}
<div
ref={menu => { this.menuRef = menu; }}
tabIndex="-1"
style={{
'width': width,
}}
className={classes([
'Dropdown__menu',
over && 'Dropdown__over',
])}>
{this.buildMenu()}
</Box>
</div>
) : null;
return (
+11 -2
View File
@@ -43,7 +43,7 @@ export class Input extends Component {
}
};
this.handleKeyDown = e => {
const { onInput, onChange } = this.props;
const { onInput, onChange, onEnter } = this.props;
if (e.keyCode === 13) {
this.setEditing(false);
if (onChange) {
@@ -52,7 +52,14 @@ export class Input extends Component {
if (onInput) {
onInput(e, e.target.value);
}
e.target.blur();
if (onEnter) {
onEnter(e, e.target.value);
}
if (this.props.selfClear) {
e.target.value = '';
} else {
e.target.blur();
}
return;
}
if (e.keyCode === 27) {
@@ -90,8 +97,10 @@ export class Input extends Component {
const { props } = this;
// Input only props
const {
selfClear,
onInput,
onChange,
onEnter,
value,
maxLength,
placeholder,
@@ -144,7 +144,10 @@ export class NumberInput extends Component {
unit,
minValue,
maxValue,
height,
width,
lineHeight,
fontSize,
format,
onChange,
onDrag,
@@ -178,6 +181,9 @@ export class NumberInput extends Component {
className,
])}
minWidth={width}
minHeight={height}
lineHeight={lineHeight}
fontSize={fontSize}
onMouseDown={this.handleDragStart}>
<div className="NumberInput__barContainer">
<div
@@ -194,6 +200,9 @@ export class NumberInput extends Component {
className="NumberInput__input"
style={{
display: !editing ? 'none' : undefined,
height: height,
'line-height': lineHeight,
'font-size': fontSize,
}}
onBlur={e => {
if (!editing) {
@@ -162,6 +162,8 @@ const Catalog = props => {
content={(data.self_paid
? Math.round(pack.cost * 1.1)
: pack.cost) + ' credits'}
tooltip={pack.desc}
tooltipPosition="left"
onClick={() => act(ref, 'add', {
id: pack.id,
})} />
@@ -64,7 +64,7 @@ export const CodexGigas = props => {
<Button
key={title.toLowerCase()}
content={title}
disabled={data.currentSection >= 2}
disabled={data.currentSection > 2}
onClick={() => act(title + ' ')} />
))}
</LabeledList.Item>
@@ -73,7 +73,7 @@ export const CodexGigas = props => {
<Button
key={name.toLowerCase()}
content={name}
disabled={data.currentSection >= 4}
disabled={data.currentSection > 4}
onClick={() => act(name)} />
))}
</LabeledList.Item>
@@ -89,7 +89,7 @@ export const CodexGigas = props => {
<LabeledList.Item label="Submit">
<Button
content="Search"
disabled={data.currentSection <= 4}
disabled={data.currentSection < 4}
onClick={() => act('search')} />
</LabeledList.Item>
</LabeledList>
@@ -0,0 +1,57 @@
import { useBackend } from '../backend';
import { Box, Button, Grid, Section, NoticeBox } from '../components';
import { toTitleCase } from 'common/string';
export const EightBallVote = props => {
const { act, data } = useBackend(props);
const {
question,
shaking,
answers = [],
} = data;
if (!shaking) {
return (
<NoticeBox>
No question is currently being asked.
</NoticeBox>
);
}
return (
<Section>
<Box
bold
textAlign="center"
fontSize="16px"
m={1}>
&quot;{question}&quot;
</Box>
<Grid>
{answers.map(answer => (
<Grid.Column key={answer.answer}>
<Button
fluid
bold
content={toTitleCase(answer.answer)}
selected={answer.selected}
fontSize="16px"
lineHeight="24px"
textAlign="center"
mb={1}
onClick={() => act('vote', {
answer: answer.answer,
})} />
<Box
bold
textAlign="center"
fontSize="30px">
{answer.amount}
</Box>
</Grid.Column>
))}
</Grid>
</Section>
);
};
@@ -0,0 +1,115 @@
import { Box, Section, Button, Grid } from '../components';
import { useBackend } from '../backend';
export const EmergencyShuttleConsole = props => {
const { act, data } = useBackend(props);
const {
timer_str,
enabled,
emagged,
engines_started,
authorizations_remaining,
authorizations = [],
} = data;
return (
<Section>
<Box
bold
fontSize="40px"
textAlign="center"
fontFamily="monospace">
{timer_str}
</Box>
<Box
textAlign="center"
fontSize="16px"
mb={1}>
<Box
inline
bold>
ENGINES:
</Box>
<Box
inline
color={engines_started ? 'good' : 'average'}
ml={1}>
{engines_started ? 'Online' : 'Idle'}
</Box>
</Box>
<Section
title="Early Launch Authorization"
level={2}
buttons={(
<Button
icon="times"
content="Repeal All"
color="bad"
disabled={!enabled}
onClick={() => act('abort')} />
)}>
<Grid>
<Grid.Column>
<Button
fluid
icon="exclamation-triangle"
color="good"
content="AUTHORIZE"
disabled={!enabled}
onClick={() => act('authorize')} />
</Grid.Column>
<Grid.Column>
<Button
fluid
icon="minus"
content="REPEAL"
disabled={!enabled}
onClick={() => act('repeal')} />
</Grid.Column>
</Grid>
<Section
title="Authorizations"
level={3}
minHeight="150px"
buttons={(
<Box
inline
bold
color={emagged ? 'bad' : 'good'}>
{emagged ? 'ERROR' : 'Remaining: ' + authorizations_remaining}
</Box>
)}>
{authorizations.length > 0 ? (
authorizations.map(authorization => (
<Box
key={authorization.name}
bold
fontSize="16px"
className="candystripe">
{authorization.name} ({authorization.job})
</Box>
))
) : (
<Box
bold
textAlign="center"
fontSize="16px"
color="average">
No Active Authorizations
</Box>
)}
{authorizations.map(authorization => (
<Box
key={authorization.name}
bold
fontSize="16px"
className="candystripe">
{authorization.name} ({authorization.job})
</Box>
))}
</Section>
</Section>
</Section>
);
};
@@ -0,0 +1,103 @@
import { useBackend } from '../backend';
import { Section, LabeledList, Button, NumberInput } from '../components';
import { Fragment } from 'inferno';
export const GulagTeleporterConsole = props => {
const { act, data } = useBackend(props);
const {
teleporter,
teleporter_lock,
teleporter_state_open,
teleporter_location,
beacon,
beacon_location,
id,
id_name,
can_teleport,
goal = 0,
prisoner = {},
} = data;
return (
<Fragment>
<Section
title="Teleporter Console"
buttons={(
<Fragment>
<Button
content={teleporter_state_open ? 'Open' : 'Closed'}
disabled={teleporter_lock}
selected={teleporter_state_open}
onClick={() => act('toggle_open')} />
<Button
icon={teleporter_lock ? 'lock' : 'unlock'}
content={teleporter_lock ? 'Locked' : 'Unlocked'}
selected={teleporter_lock}
disabled={teleporter_state_open}
onClick={() => act('teleporter_lock')}
/>
</Fragment>
)}>
<LabeledList>
<LabeledList.Item
label="Teleporter Unit"
color={teleporter ? 'good' : 'bad'}
buttons={!teleporter && (
<Button
content="Reconnect"
onClick={() => act('scan_teleporter')}
/>
)}>
{teleporter ? teleporter_location : 'Not Connected'}
</LabeledList.Item>
<LabeledList.Item
label="Receiver Beacon"
color={beacon ? 'good' : 'bad'}
buttons={!beacon && (
<Button
content="Reconnect"
onClick={() => act('scan_beacon')}
/>
)}>
{beacon ? beacon_location : 'Not Connected'}
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="Prisoner Details">
<LabeledList>
<LabeledList.Item label="Prisoner ID">
<Button
fluid
content={id ? id_name : 'No ID'}
onClick={() => act('handle_id')}
/>
</LabeledList.Item>
<LabeledList.Item label="Point Goal">
<NumberInput
value={goal}
width="48px"
minValue={1}
maxValue={1000}
onChange={(e, value) => act('set_goal', { value: value })}
/>
</LabeledList.Item>
<LabeledList.Item label="Occupant">
{prisoner.name ? prisoner.name : "No Occupant"}
</LabeledList.Item>
<LabeledList.Item label="Criminal Status">
{prisoner.crimstat ? prisoner.crimstat : 'No Status'}
</LabeledList.Item>
</LabeledList>
</Section>
<Button
fluid
content="Process Prisoner"
disabled={!can_teleport}
textAlign="center"
color="bad"
onClick={() => act('teleport')}
/>
</Fragment>
);
};
@@ -0,0 +1,72 @@
import { useBackend } from '../backend';
import { Section, LabeledList, ProgressBar, Button, BlockQuote } from '../components';
export const Intellicard = props => {
const { act, data } = useBackend(props);
const offline = (isDead || isBraindead);
const {
name,
isDead,
isBraindead,
health,
wireless,
radio,
wiping,
laws = [],
} = data;
return (
<Section
title={name || "Empty Card"}
buttons={!!name && (
<Button
icon="trash"
content={wiping ? 'Stop Wiping' : 'Wipe'}
disabled={isDead}
onClick={() => act('wipe')} />
)}>
{!!name && (
<LabeledList>
<LabeledList.Item
label="Status"
color={(offline) ? 'bad' : 'good'}>
{offline ? 'Offline' : 'Operation'}
</LabeledList.Item>
<LabeledList.Item label="Software Integrity">
<ProgressBar
value={health}
minValue={0}
maxValue={100}
ranges={{
good: [70, Infinity],
average: [50, 70],
bad: [-Infinity, 50],
}}
/>
</LabeledList.Item>
<LabeledList.Item label="Settings">
<Button
icon="signal"
content="Wireless Activity"
selected={wireless}
onClick={() => act('wireless')} />
<Button
icon="microphone"
content="Subspace Radio"
selected={radio}
onClick={() => act('radio')} />
</LabeledList.Item>
<LabeledList.Item label="Laws">
{laws.map(law => (
<BlockQuote key={law}>
{law}
</BlockQuote>
))}
</LabeledList.Item>
</LabeledList>
)}
</Section>
);
};
@@ -0,0 +1,278 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, Section, Grid, Box, Input, NoticeBox, Table, NumberInput } from '../components';
export const LaunchpadButtonPad = props => {
const { act } = useBackend(props);
return (
<Grid width="1px">
<Grid.Column>
<Button
fluid
icon="arrow-left"
iconRotation={45}
mb={1}
onClick={() => act('move_pos', {
x: -1,
y: 1,
})} />
<Button
fluid
icon="arrow-left"
mb={1}
onClick={() => act('move_pos', {
x: -1,
})} />
<Button
fluid
icon="arrow-down"
iconRotation={45}
mb={1}
onClick={() => act('move_pos', {
x: -1,
y: -1,
})} />
</Grid.Column>
<Grid.Column>
<Button
fluid
icon="arrow-up"
mb={1}
onClick={() => act('move_pos', {
y: 1,
})} />
<Button
fluid
content="R"
mb={1}
onClick={() => act('set_pos', {
x: 0,
y: 0,
})} />
<Button
fluid
icon="arrow-down"
mb={1}
onClick={() => act('move_pos', {
y: -1,
})} />
</Grid.Column>
<Grid.Column>
<Button
fluid
icon="arrow-up"
iconRotation={45}
mb={1}
onClick={() => act('move_pos', {
x: 1,
y: 1,
})} />
<Button
fluid
icon="arrow-right"
mb={1}
onClick={() => act('move_pos', {
x: 1,
})} />
<Button
fluid
icon="arrow-right"
iconRotation={45}
mb={1}
onClick={() => act('move_pos', {
x: 1,
y: -1,
})} />
</Grid.Column>
</Grid>
);
};
export const LaunchpadControl = props => {
const { topLevel } = props;
const { act, data } = useBackend(props);
const {
x,
y,
pad_name,
range,
} = data;
return (
<Section
title={(
<Input
value={pad_name}
width="170px"
onChange={(e, value) => act('rename', {
name: value,
})}
/>
)}
level={topLevel ? 1 : 2}
buttons={(
<Button
icon="times"
content="Remove"
color="bad"
onClick={() => act('remove')} />
)}>
<Grid>
<Grid.Column>
<Section title="Controls" level={2}>
<LaunchpadButtonPad state={props.state} />
</Section>
</Grid.Column>
<Grid.Column>
<Section title="Target" level={2}>
<Box fontSize="26px">
<Box mb={1}>
<Box
inline
bold
mr={1}>
X:
</Box>
<NumberInput
value={x}
minValue={-range}
maxValue={range}
lineHeight="30px"
fontSize="26px"
width="90px"
height="30px"
stepPixelSize={10}
onChange={(e, value) => act('set_pos', {
x: value,
})}
/>
</Box>
<Box>
<Box
inline
bold
mr={1}>
Y:
</Box>
<NumberInput
value={y}
minValue={-range}
maxValue={range}
stepPixelSize={10}
lineHeight="30px"
fontSize="26px"
width="90px"
height="30px"
onChange={(e, value) => act('set_pos', {
y: value,
})}
/>
</Box>
</Box>
</Section>
</Grid.Column>
</Grid>
<Grid>
<Grid.Column>
<Button
fluid
icon="upload"
content="Launch"
textAlign="center"
onClick={() => act('launch')} />
</Grid.Column>
<Grid.Column>
<Button
fluid
icon="download"
content="Pull"
textAlign="center"
onClick={() => act('pull')} />
</Grid.Column>
</Grid>
</Section>
);
};
export const LaunchpadRemote = props => {
const { data } = useBackend(props);
const {
has_pad,
pad_closed,
} = data;
if (!has_pad) {
return (
<NoticeBox>
No Launchpad Connected
</NoticeBox>
);
}
if (pad_closed) {
return (
<NoticeBox>
Launchpad Closed
</NoticeBox>
);
}
return (
<LaunchpadControl topLevel state={props.state} />
);
};
export const LaunchpadConsole = props => {
const { act, data } = useBackend(props);
const {
launchpads = [],
selected_id,
} = data;
if (launchpads.length <= 0) {
return (
<NoticeBox>
No Pads Connected
</NoticeBox>
);
}
return (
<Section>
<Grid>
<Grid.Column size={0.6}>
<Box
style={{
'border-right': '2px solid rgba(255, 255, 255, 0.1)',
}}
minHeight="190px"
mr={1}>
{launchpads.map(launchpad => (
<Button
fluid
key={launchpad.name}
content={launchpad.name}
selected={selected_id === launchpad.id}
color="transparent"
onClick={() => act('select_pad', { id: launchpad.id })}
/>
))}
</Box>
</Grid.Column>
<Grid.Column>
{selected_id ? (
<LaunchpadControl state={props.state} />
) : (
<Box>
Please select a pad
</Box>
)}
</Grid.Column>
</Grid>
</Section>
);
};
+147
View File
@@ -0,0 +1,147 @@
import { useBackend } from '../backend';
import { Section, LabeledList, Button, NumberInput, ProgressBar, Grid, Input, Dropdown } from '../components';
import { Fragment } from 'inferno';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
export const Mule = props => {
const { act, data } = useBackend(props);
const locked = data.locked && !data.siliconUser;
const {
siliconUser,
on,
cell,
cellPercent,
load,
mode,
modeStatus,
haspai,
autoReturn,
autoPickup,
reportDelivery,
destination,
home,
id,
destinations = [],
} = data;
return (
<Fragment>
<InterfaceLockNoticeBox
siliconUser={siliconUser}
locked={locked}
/>
<Section
title="Status"
minHeight="110px"
buttons={!locked && (
<Button
icon={on ? 'power-off' : 'times'}
content={on ? 'On' : 'Off'}
selected={on}
onClick={() => act('on')}
/>
)} >
<ProgressBar
value={cell ? (cellPercent / 100) : 0}
color={cell ? 'good' : 'bad'}
/>
<Grid mt={1}>
<Grid.Column>
<LabeledList>
<LabeledList.Item label="Mode" color={modeStatus}>
{mode}
</LabeledList.Item>
</LabeledList>
</Grid.Column>
<Grid.Column>
<LabeledList>
<LabeledList.Item
label="Load"
color={load ? 'good' : 'average'}>
{load || 'None'}
</LabeledList.Item>
</LabeledList>
</Grid.Column>
</Grid>
</Section>
{!locked && (
<Section
title="Controls"
buttons={(
<Fragment>
{!!load && (
<Button
icon="eject"
content="Unload"
onClick={() => act('unload')} />
)}
{!!haspai && (
<Button
icon="eject"
content="Eject PAI"
onClick={() => act('ejectpai')} />
)}
</Fragment>
)}>
<LabeledList>
<LabeledList.Item label="ID">
<Input
value={id}
onChange={(e, value) => act('setid', { value: value })}
/>
</LabeledList.Item>
<LabeledList.Item label="Destination">
<Dropdown
over
selected={destination || 'None'}
options={destinations}
width="150px"
onSelected={val => act('destination', { value: val })}
/>
<Button
icon="stop"
content="Stop"
onClick={() => act('stop')} />
<Button
icon="play"
content="Go"
onClick={() => act('go')} />
</LabeledList.Item>
<LabeledList.Item label="Home">
<Dropdown
over
selected={home}
options={destinations}
width="150px"
onSelected={val => act('destination', { value: val })} />
<Button
icon="home"
content="Go Home"
onClick={() => act('home')} />
</LabeledList.Item>
<LabeledList.Item label="Settings">
<Button.Checkbox
checked={autoReturn}
content="Auto-Return"
onClick={() => act('autored')} />
<br />
<Button.Checkbox
checked={autoPickup}
content="Auto-Pickup"
onClick={() => act('autopick')}
/>
<br />
<Button.Checkbox
checked={reportDelivery}
content="Report Delivery"
onClick={() => act('report')}
/>
</LabeledList.Item>
</LabeledList>
</Section>
)}
</Fragment>
);
};
@@ -0,0 +1,33 @@
import { useBackend } from '../backend';
import { Section, Button } from '../components';
export const NotificationPreferences = props => {
const { act, data } = useBackend(props);
const ignoresPreSort = data.ignore || [];
const ignores = ignoresPreSort.sort((a, b) => {
const descA = a.desc.toLowerCase();
const descB = b.desc.toLowerCase();
if (descA < descB) {
return -1;
}
if (descA > descB) {
return 1;
}
return 0;
});
return (
<Section title="Ghost Role Notifications">
{ignores.map(ignore => (
<Button
fluid
key={ignore.key}
icon={ignore.enabled ? 'times' : 'check'}
content={ignore.desc}
color={ignore.enabled ? 'bad' : 'good'}
onClick={() => act('toggle_ignore', { key: ignore.key })} />
))}
</Section>
);
};
@@ -0,0 +1,66 @@
import { useBackend } from '../backend';
import { Box, Button, ProgressBar, Section, AnimatedNumber } from '../components';
export const NtnetRelay = props => {
const { act, data } = useBackend(props);
const {
enabled,
dos_capacity,
dos_overload,
dos_crashed,
} = data;
return (
<Section
title="Network Buffer"
buttons={(
<Button
icon="power-off"
selected={enabled}
content={enabled ? 'ENABLED' : 'DISABLED'}
onClick={() => act('toggle')}
/>
)}>
{!dos_crashed ? (
<ProgressBar
value={dos_overload}
minValue={0}
maxValue={dos_capacity}>
<AnimatedNumber value={dos_overload} /> GQ
{' / '}
{dos_capacity} GQ
</ProgressBar>
) : (
<Box fontFamily="monospace">
<Box fontSize="20px">
NETWORK BUFFER OVERFLOW
</Box>
<Box fontSize="16px">
OVERLOAD RECOVERY MODE
</Box>
<Box>
This system is suffering temporary outage due to overflow
of traffic buffers. Until buffered traffic is processed,
all further requests will be dropped. Frequent occurences
of this error may indicate insufficient hardware capacity
of your network. Please contact your network planning
department for instructions on how to resolve this issue.
</Box>
<Box fontSize="20px" color="bad">
ADMINISTRATOR OVERRIDE
</Box>
<Box fontSize="16px" color="bad">
CAUTION - DATA LOSS MAY OCCUR
</Box>
<Button
icon="signal"
content="PURGE BUFFER"
mt={1}
color="bad"
onClick={() => act('restart')} />
</Box>
)}
</Section>
);
};
@@ -0,0 +1,90 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Flex, Icon, LabeledList, NoticeBox, ProgressBar, Section, ColorBox } from '../components';
export const NtosConfiguration = props => {
const { act, data } = useBackend(props);
const {
power_usage,
battery_exists,
battery = {},
disk_size,
disk_used,
hardware = [],
} = data;
return (
<Fragment>
<Section
title="Power Supply"
buttons={(
<Box
inline
bold
mr={1}>
Power Draw: {power_usage}W
</Box>
)}>
<LabeledList>
<LabeledList.Item
label="Battery Status"
color={!battery_exists && 'average'}>
{battery_exists ? (
<ProgressBar
value={battery.charge}
minValue={0}
maxValue={battery.max}
ranges={{
good: [battery.max / 2, Infinity],
average: [battery.max / 4, battery.max / 2],
bad: [-Infinity, battery.max / 4],
}}>
{battery.charge} / {battery.max}
</ProgressBar>
) : 'Not Available'}
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="File System">
<ProgressBar
value={disk_used}
minValue={0}
maxValue={disk_size}
color="good">
{disk_used} GQ / {disk_size} GQ
</ProgressBar>
</Section>
<Section title="Hardware Components">
{hardware.map(component => (
<Section
key={component.name}
title={component.name}
level={2}
buttons={(
<Fragment>
{!component.critical && (
<Button.Checkbox
content="Enabled"
checked={component.enabled}
mr={1}
onClick={() => act('PC_toggle_component', {
name: component.name,
})}
/>
)}
<Box
inline
bold
mr={1}>
Power Usage: {component.powerusage}W
</Box>
</Fragment>
)}>
{component.desc}
</Section>
))}
</Section>
</Fragment>
);
};
@@ -0,0 +1,170 @@
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, Grid, LabeledList, ProgressBar, Section, Input, Table, Icon, Flex } from '../components';
import { Fragment } from 'inferno';
import { createLogger } from '../logging';
const logger = createLogger('ntos chat');
export const NtosNetChat = props => {
const { act, data } = useBackend(props);
const {
can_admin,
adminmode,
authed,
username,
active_channel,
is_operator,
all_channels = [],
clients = [],
messages = [],
} = data;
const in_channel = (active_channel !== null);
const authorized = (authed || adminmode);
return (
<Section
height="600px">
<Table
height="580px">
<Table.Row>
<Table.Cell
verticalAlign="top"
style={{
width: '200px',
}}>
<Box
height="537px"
overflowY="scroll">
<Button.Input
fluid
content="New Channel..."
onCommit={(e, value) => act('PRG_newchannel', {
new_channel_name: value,
})} />
{all_channels.map(channel => (
<Button
fluid
key={channel.chan}
content={channel.chan}
selected={channel.id === active_channel}
color="transparent"
onClick={() => act('PRG_joinchannel', {
id: channel.id,
})} />
))}
</Box>
<Button.Input
fluid
mt={1}
content={username + '...'}
currentValue={username}
onCommit={(e, value) => act('PRG_changename', {
new_name: value,
})} />
{!!can_admin && (
<Button
fluid
bold
content={"ADMIN MODE: " + (adminmode ? 'ON' : 'OFF')}
color={adminmode ? 'bad' : 'good'}
onClick={() => act('PRG_toggleadmin')} />
)}
</Table.Cell>
<Table.Cell>
<Box
height="560px"
overflowY="scroll">
{in_channel && (
authorized ? (
messages.map(message => (
<Box
key={message.msg}>
{message.msg}
</Box>
))
) : (
<Box
textAlign="center">
<Icon
name="exclamation-triangle"
mt={4}
fontSize="40px" />
<Box
mt={1}
bold
fontSize="18px">
THIS CHANNEL IS PASSWORD PROTECTED
</Box>
<Box mt={1}>
INPUT PASSWORD TO ACCESS
</Box>
</Box>
)
)}
</Box>
<Input
fluid
selfClear
mt={1}
onEnter={(e, value) => act('PRG_speak', {
message: value,
})} />
</Table.Cell>
<Table.Cell
verticalAlign="top"
style={{
width: '150px',
}}>
<Box
height="477px"
overflowY="scroll">
{clients.map(client => (
<Box key={client.name}>
{client.name}
</Box>
))}
</Box>
{(in_channel && authorized) && (
<Fragment>
<Button.Input
fluid
content="Save log..."
defaultValue="new_log"
onCommit={(e, value) => act('PRG_savelog', {
log_name: value,
})} />
<Button.Confirm
fluid
content="Leave Channel"
onClick={() => act('PRG_leavechannel')} />
</Fragment>
)}
{!!is_operator && authed && (
<Fragment>
<Button.Confirm
fluid
content="Delete Channel"
onClick={() => act('PRG_deletechannel')} />
<Button.Input
fluid
content="Rename Channel..."
onCommit={(e, value) => act('PRG_renamechannel', {
new_name: value,
})} />
<Button.Input
fluid
content="Set Password..."
onCommit={(e, value) => act('PRG_setpassword', {
new_password: value,
})} />
</Fragment>
)}
</Table.Cell>
</Table.Row>
</Table>
</Section>
);
};
@@ -0,0 +1,85 @@
import { Grid, NumberInput, Button, Section } from '../components';
import { useBackend } from '../backend';
import { toFixed } from 'common/math';
export const Signaler = props => {
const { act, data } = useBackend(props);
const {
code,
frequency,
minFrequency,
maxFrequency,
} = data;
return (
<Section>
<Grid>
<Grid.Column size={1.4} color="label">
Frequency:
</Grid.Column>
<Grid.Column>
<NumberInput
animate
unit="kHz"
step={0.2}
stepPixelSize={6}
minValue={minFrequency / 10}
maxValue={maxFrequency / 10}
value={frequency / 10}
format={value => toFixed(value, 1)}
width={13}
onDrag={(e, value) => act('freq', {
freq: value,
})} />
</Grid.Column>
<Grid.Column>
<Button
ml={1.3}
icon="sync"
content="Reset"
onClick={() => act('reset', {
reset: "freq",
})} />
</Grid.Column>
</Grid>
<Grid mt={0.6}>
<Grid.Column size={1.4} color="label">
Code:
</Grid.Column>
<Grid.Column>
<NumberInput
animate
step={1}
stepPixelSize={6}
minValue={1}
maxValue={100}
value={code}
width={13}
onDrag={(e, value) => act('code', {
code: value,
})} />
</Grid.Column>
<Grid.Column>
<Button
ml={1.3}
icon="sync"
content="Reset"
onClick={() => act('reset', {
reset: "code",
})} />
</Grid.Column>
</Grid>
<Grid mt={0.8}>
<Grid.Column>
<Button
mb={-0.1}
fluid
icon="arrow-up"
content="Send Signal"
textAlign="center"
onClick={() => act('signal')} />
</Grid.Column>
</Grid>
</Section>
);
};
@@ -0,0 +1,70 @@
import { useBackend } from '../backend';
import { Section, LabeledList, ProgressBar, Button, BlockQuote, Grid, Box } from '../components';
export const BodyEntry = props => {
const { body, swapFunc } = props;
const statusMap = {
Dead: "bad",
Unconscious: "average",
Conscious: "good",
};
const occupiedMap = {
owner: "You Are Here",
stranger: "Occupied",
available: "Swap",
};
return (
<Section
title={(
<Box inline color={body.htmlcolor}>
{body.name}
</Box>
)}
level={2}
buttons={(
<Button
content={occupiedMap[body.occupied]}
selected={body.occupied === 'owner'}
color={(body.occupied === 'stranger') && 'bad'}
onClick={() => swapFunc()}
/>
)}>
<LabeledList>
<LabeledList.Item
label="Status"
bold
color={statusMap[body.status]}>
{body.status}
</LabeledList.Item>
<LabeledList.Item label="Jelly">
{body.exoticblood}
</LabeledList.Item>
<LabeledList.Item label="Location">
{body.area}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
export const SlimeBodySwapper = props => {
const { act, data } = useBackend(props);
const {
bodies = [],
} = data;
return (
<Section>
{bodies.map(body => (
<BodyEntry
key={body.name}
body={body}
swapFunc={() => act('swap', { ref: body.ref })} />
))}
</Section>
);
};
+23 -1
View File
@@ -1,6 +1,6 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, ProgressBar, Section } from '../components';
import { Box, Button, NumberInput, LabeledList, ProgressBar, Section } from '../components';
export const Smes = props => {
const { act, data } = useBackend(props);
@@ -75,6 +75,17 @@ export const Smes = props => {
onClick={() => act('input', {
adjust: -10000,
})} />
<NumberInput
value={Math.round(data.inputLevel/1000)}
unit="kW"
width="65px"
minValue={0}
maxValue={data.inputLevelMax/1000}
onChange={(e, value) => {
return act('input', {
target: value*1000,
});
}} />
<Button
icon="forward"
disabled={data.inputLevel === data.inputLevelMax}
@@ -131,6 +142,17 @@ export const Smes = props => {
onClick={() => act('output', {
adjust: -10000,
})} />
<NumberInput
value={Math.round(data.outputLevel/1000)}
unit="kW"
width="65px"
minValue={0}
maxValue={data.outputLevelMax/1000}
onChange={(e, value) => {
return act('output', {
target: value*1000,
});
}} />
<Button
icon="forward"
disabled={data.outputLevel === data.outputLevelMax}
@@ -1,17 +1,17 @@
import { toFixed } from 'common/math';
import { Fragment } from 'inferno';
import { act } from '../byond';
import { useBackend } from '../backend';
import { Box, Button, Grid, LabeledList, NumberInput, ProgressBar, Section } from '../components';
export const SolarControl = props => {
const { state } = props;
const { config, data } = state;
const { ref } = config;
const { act, data } = useBackend(props);
const {
generated,
angle,
generated_ratio,
azimuth_current,
azimuth_rate,
max_rotation_rate,
tracking_state,
tracking_rate,
connected_panels,
connected_tracker,
} = data;
@@ -23,7 +23,7 @@ export const SolarControl = props => {
<Button
icon="sync"
content="Scan for new hardware"
onClick={() => act(ref, 'refresh')} />
onClick={() => act('refresh')} />
)}>
<Grid>
<Grid.Column>
@@ -45,13 +45,13 @@ export const SolarControl = props => {
<LabeledList.Item label="Power output">
<ProgressBar
ranges={{
good: [60000, Infinity],
average: [30000, 60000],
bad: [-Infinity, 30000],
good: [0.66, Infinity],
average: [0.33, 0.66],
bad: [-Infinity, 0.33],
}}
minValue={0}
maxValue={90000}
value={generated}
maxValue={1}
value={generated_ratio}
content={generated + ' W'} />
</LabeledList.Item>
</LabeledList>
@@ -65,20 +65,20 @@ export const SolarControl = props => {
icon="times"
content="Off"
selected={tracking_state === 0}
onClick={() => act(ref, 'tracking', { mode: 0 })} />
onClick={() => act('tracking', { mode: 0 })} />
<Button
icon="clock-o"
content="Timed"
selected={tracking_state === 1}
onClick={() => act(ref, 'tracking', { mode: 1 })} />
onClick={() => act('tracking', { mode: 1 })} />
<Button
icon="sync"
content="Auto"
selected={tracking_state === 2}
disabled={!connected_tracker}
onClick={() => act(ref, 'tracking', { mode: 2 })} />
onClick={() => act('tracking', { mode: 2 })} />
</LabeledList.Item>
<LabeledList.Item label="Angle">
<LabeledList.Item label="Azimuth">
{(tracking_state === 0 || tracking_state === 1) && (
<NumberInput
width="52px"
@@ -87,28 +87,27 @@ export const SolarControl = props => {
stepPixelSize={2}
minValue={-360}
maxValue={+720}
value={angle}
format={angle => Math.round(360 + angle) % 360}
onDrag={(e, value) => act(ref, 'angle', { value })} />
value={azimuth_current}
onDrag={(e, value) => act('azimuth', { value })} />
)}
{tracking_state === 1 && (
<NumberInput
width="80px"
unit="°/h"
step={5}
stepPixelSize={2}
minValue={-7200}
maxValue={7200}
value={tracking_rate}
unit="°/m"
step={0.01}
stepPixelSize={1}
minValue={-max_rotation_rate-0.01}
maxValue={max_rotation_rate+0.01}
value={azimuth_rate}
format={rate => {
const sign = Math.sign(rate) > 0 ? '+' : '-';
return sign + toFixed(Math.abs(rate));
return sign + Math.abs(rate);
}}
onDrag={(e, value) => act(ref, 'rate', { value })} />
onDrag={(e, value) => act('azimuth_rate', { value })} />
)}
{tracking_state === 2 && (
<Box inline color="label" mt="3px">
{angle + ' °'} (auto)
{azimuth_current + ' °'} (auto)
</Box>
)}
</LabeledList.Item>
@@ -116,4 +115,4 @@ export const SolarControl = props => {
</Section>
</Fragment>
);
};
};
@@ -0,0 +1,382 @@
import { Box, Button, Section, Dimmer, Table, Icon, NoticeBox, Tabs, Grid, LabeledList } from "../components";
import { useBackend } from "../backend";
import { Fragment, Component } from "inferno";
export class FakeTerminal extends Component {
constructor(props) {
super(props);
this.timer = null;
this.state = {
currentIndex: 0,
currentDisplay: [],
};
}
tick() {
const { props, state } = this;
if (state.currentIndex <= props.allMessages.length) {
this.setState(prevState => {
return ({
currentIndex: prevState.currentIndex + 1,
});
});
const { currentDisplay } = state;
currentDisplay.push(props.allMessages[state.currentIndex]);
} else {
clearTimeout(this.timer);
setTimeout(props.onFinished, props.finishedTimeout);
}
}
componentDidMount() {
const {
linesPerSecond = 2.5,
} = this.props;
this.timer = setInterval(() => this.tick(), 1000 / linesPerSecond);
}
componentWillUnmount() {
clearTimeout(this.timer);
}
render() {
return (
<Box m={1}>
{this.state.currentDisplay.map(value => (
<Fragment key={value}>
{value}
<br />
</Fragment>
))}
</Box>
);
}
}
export const SyndContractor = props => {
const { data, act } = useBackend(props);
const terminalMessages = [
"Recording biometric data...",
"Analyzing embedded syndicate info...",
"STATUS CONFIRMED",
"Contacting syndicate database...",
"Awaiting response...",
"Awaiting response...",
"Awaiting response...",
"Awaiting response...",
"Awaiting response...",
"Awaiting response...",
"Response received, ack 4851234...",
"CONFIRM ACC " + (Math.round(Math.random() * 20000)),
"Setting up private accounts...",
"CONTRACTOR ACCOUNT CREATED",
"Searching for available contracts...",
"Searching for available contracts...",
"Searching for available contracts...",
"Searching for available contracts...",
"CONTRACTS FOUND",
"WELCOME, AGENT",
];
const infoEntries = [
"SyndTract v2.0",
"",
"We've identified potentional high-value targets that are",
"currently assigned to your mission area. They are believed",
"to hold valuable information which could be of immediate",
"importance to our organisation.",
"",
"Listed below are all of the contracts available to you. You",
"are to bring the specified target to the designated",
"drop-off, and contact us via this uplink. We will send",
"a specialised extraction unit to put the body into.",
"",
"We want targets alive - but we will sometimes pay slight",
"amounts if they're not, you just won't recieve the shown",
"bonus. You can redeem your payment through this uplink in",
"the form of raw telecrystals, which can be put into your",
"regular Syndicate uplink to purchase whatever you may need.",
"We provide you with these crystals the moment you send the",
"target up to us, which can be collected at anytime through",
"this system.",
"",
"Targets extracted will be ransomed back to the station once",
"their use to us is fulfilled, with us providing you a small",
"percentage cut. You may want to be mindful of them",
"identifying you when they come back. We provide you with",
"a standard contractor loadout, which will help cover your",
"identity.",
];
const errorPane = !!data.error && (
<Dimmer>
<Box
backgroundColor="red"
minHeight="150px"
mt={30}
ml={15}
mr={15}>
<Table m={1}>
<Table.Row>
<Table.Cell collapsing fontSize="100px">
<Icon
name="exclamation-triangle"
mt={4}
ml={2} />
</Table.Cell>
<Table.Cell
verticalAlign="top"
textAlign="center">
<Box
m={1}
textAlign="left"
width="100%"
minHeight="110px">
{data.error}
</Box>
<Button
content="Dismiss"
onClick={() => act('PRG_clear_error')} />
</Table.Cell>
</Table.Row>
</Table>
</Box>
</Dimmer>
);
if (!data.logged_in) {
return (
<Section minHeight="525px">
<Box
width="100%"
textAlign="center">
<Button
content="REGISTER USER"
color="transparent"
onClick={() => act('PRG_login')} />
</Box>
{!!data.error && (
<NoticeBox>
{data.error}
</NoticeBox>
)}
</Section>
);
}
if (data.logged_in && data.first_load) {
return (
<Box
backgroundColor="rgba(0, 0, 0, 0.8)"
minHeight="525px">
<FakeTerminal
allMessages={terminalMessages}
finishedTimeout={3000}
onFinished={() => act('PRG_set_first_load_finished')} />
</Box>
);
}
if (data.info_screen) {
return (
<Fragment>
<Box
backgroundColor="rgba(0, 0, 0, 0.8)"
minHeight="500px">
<FakeTerminal
allMessages={infoEntries}
linesPerSecond={10} />
</Box>
<Button
fluid
content="CONTINUE"
color="transparent"
textAlign="center"
onClick={() => act('PRG_toggle_info')} />
</Fragment>
);
}
return (
<Fragment>
{errorPane}
<SyndPane state={props.state} />
</Fragment>
);
};
export const StatusPane = props => {
const { act, data } = useBackend(props);
return (
<Section
title={(
<Fragment>
Contractor Status
<Button
content="View Information Again"
color="transparent"
mb={0}
ml={1}
onClick={() => act('PRG_toggle_info')} />
</Fragment>
)}
buttons={(
<Box bold mr={1}>
{data.contract_rep} Rep
</Box>
)}>
<Grid>
<Grid.Column size={0.85}>
<LabeledList>
<LabeledList.Item
label="TC Availible"
buttons={(
<Button
content="Claim"
disabled={data.redeemable_tc <= 0}
onClick={() => act('PRG_redeem_TC')} />
)}>
{data.redeemable_tc}
</LabeledList.Item>
<LabeledList.Item label="TC Earned">
{data.earned_tc}
</LabeledList.Item>
</LabeledList>
</Grid.Column>
<Grid.Column>
<LabeledList>
<LabeledList.Item label="Contracts Completed">
{data.contracts_completed}
</LabeledList.Item>
<LabeledList.Item label="Current Status">
ACTIVE
</LabeledList.Item>
</LabeledList>
</Grid.Column>
</Grid>
</Section>
);
};
export const SyndPane = props => {
const { act, data } = useBackend(props);
const contractor_hub_items = data.contractor_hub_items || [];
const contracts = data.contracts || [];
return (
<Fragment>
<StatusPane state={props.state} />
<Tabs>
<Tabs.Tab label="Contracts">
<Section
title="Availible Contracts"
buttons={(
<Button
content="Call Extraction"
disabled={!data.ongoing_contract || data.extraction_enroute}
onClick={() => act('PRG_call_extraction')} />
)}>
{contracts.map(contract => {
const active = (contract.status > 1);
if (contract.status >= 5) {
return;
}
return (
<Section
key={contract.target}
title={`${contract.target} (${contract.target_rank})`}
level={active ? 1 : 2}
buttons={(
<Fragment>
<Box
inline
bold
mr={1}>
{contract.payout} (+{contract.payout_bonus}) TC
</Box>
<Button
content={active ? "Abort" : "Accept"}
disabled={contract.extraction_enroute}
color={active && "bad"}
onClick={() => act(
'PRG_contract' + (active ? '_abort' : '-accept'),
{
contract_id: contract.id,
})} />
</Fragment>
)}>
<Grid>
<Grid.Column>
{contract.message}
</Grid.Column>
<Grid.Column size={0.5}>
<Box
bold
mb={1}>
Dropoff Location:
</Box>
<Box>
{contract.dropoff}
</Box>
</Grid.Column>
</Grid>
</Section>
);
})}
</Section>
</Tabs.Tab>
<Tabs.Tab label="Uplink">
<Section>
{contractor_hub_items.map(item => {
const repInfo = item.cost ? (item.cost + ' Rep') : 'FREE';
const limited = (item.limited !== -1);
return (
<Section
key={item.name}
title={item.name + ' - ' + repInfo}
level={2}
buttons={(
<Fragment>
{limited && (
<Box
inline
bold
mr={1}>
{item.limited} remaining
</Box>
)}
<Button
content="Purchase"
disabled={data.redeemable_tc < item.cost
|| (limited && item.limited <= 0)}
onClick={() => act('buy_hub', {
item: item.name,
cost: item.cost,
})} />
</Fragment>
)}>
<Table>
<Table.Row>
<Table.Cell>
<Icon
fontSize="60px"
name={item.item_icon} />
</Table.Cell>
<Table.Cell
verticalAlign="top">
{item.desc}
</Table.Cell>
</Table.Row>
</Table>
</Section>
);
})}
</Section>
</Tabs.Tab>
</Tabs>
</Fragment>
);
};
@@ -0,0 +1,69 @@
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components';
export const Teleporter = props => {
const { act, data } = useBackend(props);
const {
calibrated,
calibrating,
power_station,
regime_set,
teleporter_hub,
target,
} = data;
return (
<Section>
{!power_station && (
<Box color="bad" textAlign="center">
No power station linked.
</Box>
) || (!teleporter_hub && (
<Box color="bad" textAlign="center">
No hub linked.
</Box>
)) || (
<LabeledList>
<LabeledList.Item label="Current Regime"
buttons={(
<Button
icon="tools"
content="Change Regime"
onClick={() => act('regimeset')} />
)}>
{regime_set}
</LabeledList.Item>
<LabeledList.Item label="Current Target"
buttons={(
<Button
icon="tools"
content={"Set Target"}
onClick={() => act('settarget')} />
)}>
{target}
</LabeledList.Item>
<LabeledList.Item label="Calibration"
buttons={(
<Button
icon="tools"
content={"Calibrate Hub"}
onClick={() => act('calibrate')} />
)}>
{calibrating && (
<Box color="average">
In Progress
</Box>
) || (calibrated && (
<Box color="good">
Optimal
</Box>
) || (
<Box color="bad">
Sub-Optimal
</Box>
))}
</LabeledList.Item>
</LabeledList>
)}
</Section>
);
};
+16 -13
View File
@@ -26,27 +26,30 @@ export const Vending = props => {
}
return (
<Fragment>
<Section title="User">
{data.user && (
<Box>
{data.onstation && (
<Section title="User">
{data.user && (
<Box>
Welcome, <b>{data.user.name}</b>,
{' '}
<b>{data.user.job || "Unemployed"}</b>!
<br />
{' '}
<b>{data.user.job || "Unemployed"}</b>!
<br />
Your balance is <b>{data.user.cash} credits</b>.
</Box>
) || (
<Box color="light-gray">
</Box>
) || (
<Box color="light-gray">
No registered ID card!<br />
Please contact your local HoP!
</Box>
)}
</Section>
</Box>
)}
</Section>
)}
<Section title="Products" >
<Table>
{inventory.map((product => {
const free = (
product.price === 0
!data.onstation
|| product.price === 0
|| (
!product.premium
&& data.department
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -24,6 +24,8 @@
overflow-y: auto;
z-index: 5;
width: 100px;
max-height: 200px;
overflow-y: scroll;
border-radius: 0 0 2px 2px;
background-color: #000;
background-color: rgba(0, 0, 0, 0.75);
+4
View File
@@ -41,3 +41,7 @@
background-position: center;
background-repeat: no-repeat;
}
.action_test {
display: inherit;
}
@@ -0,0 +1,46 @@
@use 'sass:color';
@use 'sass:meta';
@use '../colors.scss' with (
$primary: #000000,
$fg-map-keys: (),
$bg-map-keys: (),
);
@use '../base.scss' with (
$color-bg: #117039,
$color-bg-grad-spread: 0%,
$border-radius: 0px,
);
//Made for the roulette table, probably requires a bunch of manual hacks to work for anything else
.theme-cardtable {
// Atomic classes
@include meta.load-css('../atomic/color.scss');
// Components
@include meta.load-css('../components/Button.scss', $with: (
'color-default': #117039,
'color-disabled': #363636,
'color-selected': #9d0808,
'color-caution': #be6209,
'color-danger': #9a9d00,
));
@include meta.load-css('../components/Layout.scss');
@include meta.load-css('../components/NumberInput.scss', $with: (
'border-color': #FFFFFF,
));
@include meta.load-css('../components/ProgressBar.scss', $with: (
'color-background': rgba(0, 0, 0, 0.5),
));
@include meta.load-css('../components/Section.scss');
@include meta.load-css('../components/TitleBar.scss', $with: (
'color-background': #381608,
));
.Button {
border-color: #FFFFFF;
border-width: 2px;
border-style: solid;
}
}
@@ -0,0 +1,49 @@
@use 'sass:color';
@use 'sass:meta';
@use '../colors.scss' with (
$primary: #00ff00,
$fg-map-keys: (),
$bg-map-keys: (),
);
@use '../base.scss' with (
$color-bg: #121b12,
$color-bg-grad-spread: 0%,
$border-radius: 2px,
);
.theme-hackerman {
// Atomic classes
@include meta.load-css('../atomic/color.scss');
// Components
@include meta.load-css('../components/Button.scss', $with: (
'color-default': colors.$primary,
'color-disabled': #4A6A4A,
'color-selected': #00FF00,
));
@include meta.load-css('../components/Input.scss', $with: (
'border-color': colors.$primary,
));
@include meta.load-css('../components/Layout.scss');
@include meta.load-css('../components/Section.scss');
@include meta.load-css('../components/TitleBar.scss', $with: (
'color-background': #223d22,
));
.Layout__content {
background-image: none;
}
.Button {
font-family: monospace;
border-width: 2px;
border-style: outset;
border-color: #00AA00;
outline: 1px solid rgb(0, 122, 0);
}
.candystripe:nth-child(odd) {
background-color: rgba(0, 100, 0, 0.50);
}
}
@@ -25,13 +25,16 @@
'color-danger': #9a9d00,
));
@include meta.load-css('../components/Input.scss', $with: (
'border-color': colors.$primary,
'border-color': #87ce87,
));
@include meta.load-css('../components/Layout.scss');
@include meta.load-css('../components/NoticeBox.scss', $with: (
'color-first': #750000,
'color-second': #910101,
));
@include meta.load-css('../components/NumberInput.scss', $with: (
'border-color': #87ce87,
));
@include meta.load-css('../components/ProgressBar.scss', $with: (
'color-background': rgba(0, 0, 0, 0.5),
));
+3 -1
View File
@@ -20,9 +20,11 @@ module.exports = (env = {}, argv) => {
entry: {
tgui: [
path.resolve(__dirname, './styles/main.scss'),
path.resolve(__dirname, './styles/themes/cardtable.scss'),
path.resolve(__dirname, './styles/themes/ntos.scss'),
path.resolve(__dirname, './styles/themes/syndicate.scss'),
path.resolve(__dirname, './styles/themes/hackerman.scss'),
path.resolve(__dirname, './styles/themes/retro.scss'),
path.resolve(__dirname, './styles/themes/syndicate.scss'),
path.resolve(__dirname, './index.js'),
],
},