From adc8d16241688bf9fba0c88e9f1df2c1fcd616e9 Mon Sep 17 00:00:00 2001 From: ShadowLarkens Date: Sat, 26 Apr 2025 11:52:14 -0700 Subject: [PATCH] TEG & Disposal Bin UI (#17602) * Add pretty spinners to the TEG * Overhaul Disposal Bin UI --- tgui/packages/tgui/interfaces/DisposalBin.tsx | 410 ++++++++++++++---- tgui/packages/tgui/interfaces/TEGenerator.tsx | 106 +++-- 2 files changed, 407 insertions(+), 109 deletions(-) diff --git a/tgui/packages/tgui/interfaces/DisposalBin.tsx b/tgui/packages/tgui/interfaces/DisposalBin.tsx index af40f0a1556..b603799419d 100644 --- a/tgui/packages/tgui/interfaces/DisposalBin.tsx +++ b/tgui/packages/tgui/interfaces/DisposalBin.tsx @@ -1,12 +1,7 @@ +import { useEffect, useState } from 'react'; import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; -import { - Box, - Button, - LabeledList, - ProgressBar, - Section, -} from 'tgui-core/components'; +import { Box, Icon, RoundGauge, Section, Stack } from 'tgui-core/components'; import type { BooleanLike } from 'tgui-core/react'; type Data = { @@ -20,97 +15,344 @@ type Data = { export const DisposalBin = (props) => { const { act, data } = useBackend(); const { mode, pressure, isAI, panel_open, flushing } = data; - let stateColor; let stateText; if (mode === 2) { - stateColor = 'good'; stateText = 'Ready'; } else if (mode <= 0) { - stateColor = 'bad'; stateText = 'N/A'; } else if (mode === 1) { - stateColor = 'average'; stateText = 'Pressurizing'; } else { - stateColor = 'average'; stateText = 'Idle'; } + return ( - + -
- - Status - - - - {stateText} - - - - - - - Controls - - - - - - - - - - - - - - +
+ + + + + + + Flush + + + + + + ''} + /> + + + + + + + + + + + + + + + act(mode ? 'pumpOff' : 'pumpOn')} + /> + + E-Stop + + +
); }; + +const EjectButton = (props) => { + const { act, data } = useBackend(); + const { isAI } = data; + const [buttonPressed, setButtonPressed] = useState(false); + + useEffect(() => { + if (!buttonPressed) { + return; + } + + const timer = setTimeout(() => { + setButtonPressed(false); + }, 250); + + return () => { + clearTimeout(timer); + }; + }, [buttonPressed]); + + return ( + + + { + act('eject'); + setButtonPressed(true); + }} + > + + + + Eject + + ); +}; + +const GiantLever = (props) => { + const { act, data } = useBackend(); + + return ( + + {/* Track 1 */} + + {/* Lever */} + + {/* Handle */} + act(data.flushing ? 'disengageHandle' : 'engageHandle')} + style={{ + background: 'linear-gradient(180deg,rgb(141, 17, 13), #200e0d)', + cursor: 'pointer', + transition: 'transform .5s cubic-bezier(0.600, 0.040, 0.980, 0.335)', + transform: data.flushing ? 'translateY(43px)' : 'none', + }} + /> + + ); +}; + +const Lights = (props: { state: string }) => { + const { state } = props; + return ( + + + + + + + + + ); +}; + +const EStopSvg = (props: React.SVGProps) => { + return ( + + + + + + + + + + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/TEGenerator.tsx b/tgui/packages/tgui/interfaces/TEGenerator.tsx index 453e4653c06..9a25b3cb4a0 100644 --- a/tgui/packages/tgui/interfaces/TEGenerator.tsx +++ b/tgui/packages/tgui/interfaces/TEGenerator.tsx @@ -1,7 +1,10 @@ +import { useEffect, useState } from 'react'; import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; import { + AnimatedNumber, Box, + Icon, LabeledList, ProgressBar, Section, @@ -14,11 +17,11 @@ type Data = { totalOutput: number; maxTotalOutput: number; thermalOutput: number; - primary: circulator; - secondary: circulator; + primary: Circulator; + secondary: Circulator; }; -type circulator = { +type Circulator = { dir: string; output: number; flowCapacity: number; @@ -35,7 +38,7 @@ export const TEGenerator = (props) => { data; return ( - +
@@ -69,7 +72,7 @@ export const TEGenerator = (props) => { ); }; -const TEGCirculator = (props: { name: string; values: circulator }) => { +const TEGCirculator = (props: { name: string; values: Circulator }) => { const { name, values } = props; const { @@ -84,26 +87,79 @@ const TEGCirculator = (props: { name: string; values: circulator }) => { return (
- - - {formatPower(output)} - - - {toFixed(flowCapacity, 2)}% - - - {formatSiUnit(inletPressure * 1000, 0, 'Pa')} - - - {toFixed(inletTemperature, 2)} K - - - {formatSiUnit(outletPressure * 1000, 0, 'Pa')} - - - {toFixed(outletTemperature, 2)} K - - + + + + + + + + + Flow Capacity + + + val.toFixed(2) + '%'} + /> + + + + + + + + + {formatPower(output)} + + + {formatSiUnit(inletPressure * 1000, 0, 'Pa')} + + + {toFixed(inletTemperature, 2)} K + + + {formatSiUnit(outletPressure * 1000, 0, 'Pa')} + + + {toFixed(outletTemperature, 2)} K + + + +
); }; + +const CirculatorFanSpinner = (props: { value: number }) => { + const { value } = props; + + const [rotation, setRotation] = useState(0); + + const SPEED_MULTIPLIER = 0.2; + const FLOW_MODIFIER = 4; + const STEP_SIZE = 4; + + useEffect(() => { + // Only spin if there's ~some~ flow. + if (!value) { + return; + } + const id = setInterval( + () => { + setRotation((rot) => (rot + STEP_SIZE) % 359); + }, + SPEED_MULTIPLIER * (100 * FLOW_MODIFIER - value * FLOW_MODIFIER), + ); + return () => clearInterval(id); + }, [value]); + + return ( + 80 ? 'good' : value > 50 ? 'average' : 'bad'} + /> + ); +};