mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
new! ui
This commit is contained in:
188
tgui/packages/tgui/interfaces/Biogenerator.js
Normal file
188
tgui/packages/tgui/interfaces/Biogenerator.js
Normal file
@@ -0,0 +1,188 @@
|
||||
import { classes } from 'common/react';
|
||||
import { createSearch } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Dimmer, Flex, Icon, Input, Section, Table, Tabs, NoticeBox, NumberInput } from '../components';
|
||||
import { formatMoney } from '../format';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const MAX_SEARCH_RESULTS = 25;
|
||||
|
||||
export const Biogenerator = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const {
|
||||
beaker,
|
||||
processing,
|
||||
} = data;
|
||||
return (
|
||||
<Window resizable>
|
||||
{!!processing && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin={1} />
|
||||
{' Processing...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
<Window.Content scrollable>
|
||||
{!beaker && (
|
||||
<NoticeBox>No Container</NoticeBox>
|
||||
)}
|
||||
{!!beaker && (
|
||||
<BiogeneratorContent />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const BiogeneratorContent = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
biomass,
|
||||
can_process,
|
||||
categories = [],
|
||||
} = data;
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
const [
|
||||
selectedCategory,
|
||||
setSelectedCategory,
|
||||
] = useLocalState(context, 'category', categories[0]?.name);
|
||||
const testSearch = createSearch(searchText, item => {
|
||||
return item.name;
|
||||
});
|
||||
const items = searchText.length > 0
|
||||
// Flatten all categories and apply search to it
|
||||
&& categories
|
||||
.flatMap(category => category.items || [])
|
||||
.filter(testSearch)
|
||||
.filter((item, i) => i < MAX_SEARCH_RESULTS)
|
||||
// Select a category and show all items in it
|
||||
|| categories
|
||||
.find(category => category.name === selectedCategory)
|
||||
?.items
|
||||
// If none of that results in a list, return an empty list
|
||||
|| [];
|
||||
return (
|
||||
<Section
|
||||
title={(
|
||||
<Box
|
||||
inline
|
||||
color={biomass > 0 ? 'good' : 'bad'}>
|
||||
{formatMoney(biomass)} Biomass
|
||||
</Box>
|
||||
)}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
Search
|
||||
<Input
|
||||
value={searchText}
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
mx={1} />
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject"
|
||||
onClick={() => act('detach')} />
|
||||
<Button
|
||||
icon="cog"
|
||||
content="Activate"
|
||||
disabled={!can_process}
|
||||
onClick={() => act('activate')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Flex>
|
||||
{searchText.length === 0 && (
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category.name}
|
||||
selected={category.name === selectedCategory}
|
||||
onClick={() => setSelectedCategory(category.name)}>
|
||||
{category.name} ({category.items?.length || 0})
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
)}
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
{items.length === 0 && (
|
||||
<NoticeBox>
|
||||
{searchText.length === 0
|
||||
? 'No items in this category.'
|
||||
: 'No results found.'}
|
||||
</NoticeBox>
|
||||
)}
|
||||
<Table>
|
||||
<ItemList
|
||||
biomass={biomass}
|
||||
items={items} />
|
||||
</Table>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemList = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const [
|
||||
hoveredItem,
|
||||
setHoveredItem,
|
||||
] = useLocalState(context, 'hoveredItem', {});
|
||||
const hoveredCost = hoveredItem && hoveredItem.cost || 0;
|
||||
// Append extra hover data to items
|
||||
const items = props.items.map(item => {
|
||||
const [
|
||||
amount,
|
||||
setAmount,
|
||||
] = useLocalState(context, "amount" + item.name, 1);
|
||||
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
|
||||
const notEnoughHovered = props.biomass - hoveredCost
|
||||
* hoveredItem.amount < item.cost * amount;
|
||||
const disabledDueToHovered = notSameItem && notEnoughHovered;
|
||||
const disabled = props.biomass < item.cost * amount || disabledDueToHovered;
|
||||
return {
|
||||
...item,
|
||||
disabled,
|
||||
amount,
|
||||
setAmount,
|
||||
};
|
||||
});
|
||||
return items.map(item => (
|
||||
<Table.Row key={item.id}>
|
||||
<Table.Cell>
|
||||
<span
|
||||
className={classes(['design32x32', item.id])}
|
||||
style={{
|
||||
'vertical-align': 'middle',
|
||||
}} />
|
||||
{' '}<b>{item.name}</b>
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing>
|
||||
<NumberInput
|
||||
value={Math.round(item.amount)}
|
||||
width="35px"
|
||||
minValue={1}
|
||||
maxValue={10}
|
||||
onChange={(e, value) => item.setAmount(value)} />
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing>
|
||||
<Button
|
||||
style={{
|
||||
'text-align': 'right',
|
||||
}}
|
||||
fluid
|
||||
content={item.cost * item.amount + ' ' + "BIO"}
|
||||
disabled={item.disabled}
|
||||
onmouseover={() => setHoveredItem(item)}
|
||||
onmouseout={() => setHoveredItem({})}
|
||||
onClick={() => act('create', {
|
||||
id: item.id,
|
||||
amount: item.amount,
|
||||
})} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
));
|
||||
};
|
||||
107
tgui/packages/tgui/interfaces/MafiaPanel.js
Normal file
107
tgui/packages/tgui/interfaces/MafiaPanel.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Flex, Button, LabeledList, Section, Box, Table, TimeDisplay } from '../components';
|
||||
import { Fragment } from 'inferno';
|
||||
import { Window } from '../layouts';
|
||||
import { FlexItem } from '../components/Flex';
|
||||
|
||||
export const MafiaPanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
players,
|
||||
actions,
|
||||
phase,
|
||||
role_info,
|
||||
admin_controls,
|
||||
timeleft,
|
||||
all_roles } = data;
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content>
|
||||
<Section title={phase}>
|
||||
{!!role_info && (
|
||||
<Table>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<TimeDisplay auto="down" value={timeleft} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell bold>
|
||||
You are a {role_info.role}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row bold>
|
||||
<Table.Cell>
|
||||
{role_info.desc}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{!!role_info.action_log && role_info.action_log.map(log_line => (
|
||||
<Table.Row key={log_line}>
|
||||
<Table.Cell>
|
||||
{role_info.action_log}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
)}
|
||||
</Section>
|
||||
<Flex>
|
||||
{!!actions && actions.map(action => {
|
||||
return (
|
||||
<Flex.Item key={action}>
|
||||
<Button
|
||||
onClick={() => act("mf_action", { atype: action })}>
|
||||
{action}
|
||||
</Button>
|
||||
</Flex.Item>);
|
||||
})}
|
||||
{ !!admin_controls && (
|
||||
<Fragment>
|
||||
<Flex.Item>
|
||||
<Button onClick={() => act("next_phase")}>Next Phase</Button>
|
||||
</Flex.Item>
|
||||
<FlexItem>
|
||||
<Button onClick={() => act("new_game")}>New Game</Button>
|
||||
</FlexItem>
|
||||
</Fragment>)}
|
||||
</Flex>
|
||||
<Section title="Players">
|
||||
<LabeledList>
|
||||
{!!players && players.map(player => { return (
|
||||
<LabeledList.Item
|
||||
className="candystripe"
|
||||
key={player.ref}
|
||||
label={player.name}>
|
||||
{player.votes !== undefined
|
||||
&& (<Fragment>Votes : {player.votes} </Fragment>)}
|
||||
{
|
||||
!!player.actions && player.actions.map(action => {
|
||||
return (
|
||||
<Button
|
||||
key={action}
|
||||
onClick={
|
||||
// eslint-disable-next-line indent
|
||||
() => act("mf_targ_action", { atype: action, target: player.ref })
|
||||
}>
|
||||
{action}
|
||||
</Button>); })
|
||||
}
|
||||
</LabeledList.Item>);
|
||||
})}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Roles">
|
||||
<Table>
|
||||
{!!all_roles && all_roles.map(r => (
|
||||
<Table.Row key={r}>
|
||||
<Table.Cell bold>
|
||||
{r}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, LabeledList, NoticeBox, Section } from '../components';
|
||||
import { NtosWindow } from '../layouts';
|
||||
import { NtosCyborgRemoteMonitorContent } from './NtosCyborgRemoteMonitor';
|
||||
|
||||
export const NtosCyborgRemoteMonitorSyndicate = (props, context) => {
|
||||
return (
|
||||
<NtosWindow theme="syndicate">
|
||||
<NtosWindow.Content scrollable>
|
||||
<NtosCyborgRemoteMonitorContent />
|
||||
</NtosWindow.Content>
|
||||
</NtosWindow>
|
||||
);
|
||||
};
|
||||
89
tgui/packages/tgui/interfaces/SeedExtractor.js
Normal file
89
tgui/packages/tgui/interfaces/SeedExtractor.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import { sortBy } from 'common/collections';
|
||||
import { flow } from 'common/fp';
|
||||
import { toTitleCase } from 'common/string';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, Section, Table } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
/**
|
||||
* This method takes a seed string and splits the values
|
||||
* into an object
|
||||
*/
|
||||
const splitSeedString = text => {
|
||||
const re = /([^;=]+)=([^;]+)/g;
|
||||
const ret = {};
|
||||
let m;
|
||||
do {
|
||||
m = re.exec(text);
|
||||
if (m) {
|
||||
ret[m[1]] = m[2] + '';
|
||||
}
|
||||
} while (m);
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method splits up the string "name" we get for the seeds
|
||||
* and creates an object from it include the value that is the
|
||||
* ammount
|
||||
*
|
||||
* @returns {any[]}
|
||||
*/
|
||||
const createSeeds = seedStrings => {
|
||||
const objs = Object.keys(seedStrings).map(key => {
|
||||
const obj = splitSeedString(key);
|
||||
obj.amount = seedStrings[key];
|
||||
obj.key = key;
|
||||
obj.name = toTitleCase(obj.name.replace('pack of ', ''));
|
||||
return obj;
|
||||
});
|
||||
return flow([
|
||||
sortBy(item => item.name),
|
||||
])(objs);
|
||||
};
|
||||
|
||||
export const SeedExtractor = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const seeds = createSeeds(data.seeds);
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Stored seeds:">
|
||||
<Table cellpadding="3" textAlign="center">
|
||||
<Table.Row>
|
||||
<Table.Cell>Name</Table.Cell>
|
||||
<Table.Cell>Lifespan</Table.Cell>
|
||||
<Table.Cell>Endurance</Table.Cell>
|
||||
<Table.Cell>Maturation</Table.Cell>
|
||||
<Table.Cell>Production</Table.Cell>
|
||||
<Table.Cell>Yield</Table.Cell>
|
||||
<Table.Cell>Potency</Table.Cell>
|
||||
<Table.Cell>Instability</Table.Cell>
|
||||
<Table.Cell>Stock</Table.Cell>
|
||||
</Table.Row>
|
||||
{seeds.map(item => (
|
||||
<Table.Row key={item.key}>
|
||||
<Table.Cell bold>{item.name}</Table.Cell>
|
||||
<Table.Cell>{item.lifespan}</Table.Cell>
|
||||
<Table.Cell>{item.endurance}</Table.Cell>
|
||||
<Table.Cell>{item.maturation}</Table.Cell>
|
||||
<Table.Cell>{item.production}</Table.Cell>
|
||||
<Table.Cell>{item.yield}</Table.Cell>
|
||||
<Table.Cell>{item.potency}</Table.Cell>
|
||||
<Table.Cell>{item.instability}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
content="Vend"
|
||||
onClick={() => act('select', {
|
||||
item: item.key,
|
||||
})} />
|
||||
({item.amount} left)
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user