Another SmartFridge restyle (#89013)

## About The Pull Request
Another SmartFridge restyle aimed at improving the UI/UX.

Finally used the new component (ImageButton) as I originally wanted to,
but I kept failing.

If you need to get a certain amount of fruit, you don't have to type in
how much you need and then click on the fruit, just type it in and the
fruit will be dispensed immediately, example in the video below.

There are a total of 3 options to get what you need with SmartFridge:
1. Just click on the product, you will get 1 unit, nothing new.
2. Enter the required value, this was already there but it became a bit
more convenient because you don't have to click on the product after
entering it.
3. Press Right Mouse Button to get everything at once.

## Why It's Good For The Game
Better UI/UX (No offense to those who made the past variations, they
tried their best, but now there is a special component for such
purposes, which makes it very much easier)

## Demonstration
<details><summary> Comparison screenshots </summary>

| Old (Grid) | New (Grid) | 
| - | - |
|
![image](https://github.com/user-attachments/assets/e4ee68fd-c984-402b-a265-864c08f17771)
|
![image](https://github.com/user-attachments/assets/a5334f86-22a4-4509-a6c7-09e5b39cc113)
|

| Old (List) | New (List)
| - | - |
|
![image](https://github.com/user-attachments/assets/ef5e4a29-470d-4c5e-9776-5bf2af508fb5)
|
![image](https://github.com/user-attachments/assets/653ad2f0-247e-4002-8f1f-93b76c77ff67)
|

</details>
<details><summary> Video </summary>


https://github.com/user-attachments/assets/c8788707-5947-4f1e-8a3c-6a56ac983e98

</details>

## Changelog

🆑
qol: SmartFridge got another redesign, and you can dispense all amount
of product in one click (RMB on product)
/🆑
This commit is contained in:
Aylong
2025-01-17 18:20:48 -08:00
committed by GitHub
parent 3374273730
commit 1c8def7837
6 changed files with 149 additions and 203 deletions
+1 -1
View File
@@ -12,6 +12,6 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tgui": "workspace:*",
"tgui-core": "^1.7.2"
"tgui-core": "^1.7.4"
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tgui": "workspace:*",
"tgui-core": "^1.7.2",
"tgui-core": "^1.7.4",
"tgui-dev-server": "workspace:*",
"tgui-polyfill": "workspace:*"
}
+1 -1
View File
@@ -9,7 +9,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tgui": "workspace:*",
"tgui-core": "^1.7.2",
"tgui-core": "^1.7.4",
"tgui-polyfill": "workspace:*"
}
}
+137 -191
View File
@@ -1,8 +1,7 @@
import { useState } from 'react';
import { DmIcon, Icon } from 'tgui-core/components';
import {
Box,
Button,
ImageButton,
Input,
NoticeBox,
NumberInput,
@@ -48,44 +47,57 @@ export const SmartVend = (props) => {
? Object.values(data.contents).filter(search)
: Object.values(data.contents);
return (
<Window width={498} height={550}>
<Window width={431} height={575}>
<Window.Content>
<Section
fill
scrollable
title="Storage"
buttons={
data.isdryer ? (
<Button
icon={data.drying ? 'stop' : 'tint'}
onClick={() => act('Dry')}
>
{data.drying ? 'Stop drying' : 'Dry'}
</Button>
) : (
<>
<Input
autoFocus
placeholder={'Search...'}
value={searchText}
onInput={(e, value) => setSearchText(value)}
/>
<>
{data.isdryer ? (
<Button
icon={displayMode === MODE.tile ? 'list' : 'border-all'}
tooltip={
displayMode === MODE.tile
? 'Display as a list'
: 'Display as a grid'
}
tooltipPosition="bottom"
onClick={() =>
setDisplayMode(
displayMode === MODE.tile ? MODE.list : MODE.tile,
)
}
/>
</>
)
icon={data.drying ? 'stop' : 'tint'}
onClick={() => act('Dry')}
>
{data.drying ? 'Stop drying' : 'Dry'}
</Button>
) : (
<>
<Input
autoFocus
placeholder={'Search...'}
value={searchText}
onInput={(e, value) => setSearchText(value)}
/>
<Button
icon={displayMode === MODE.tile ? 'list' : 'border-all'}
tooltip={
displayMode === MODE.tile
? 'Display as a list'
: 'Display as a grid'
}
tooltipPosition="bottom"
onClick={() =>
setDisplayMode(
displayMode === MODE.tile ? MODE.list : MODE.tile,
)
}
/>
</>
)}
<Button
icon="question"
tooltip={
<>
LMB - Vend selected amount
<br />
RMB - Vend all
</>
}
tooltipPosition={'bottom-end'}
/>
</>
}
>
{!contents.length ? (
@@ -107,173 +119,107 @@ export const SmartVend = (props) => {
const ItemTile = ({ item }) => {
const { act } = useBackend<Data>();
const [itemCount, setItemCount] = useState(1);
const fallback = (
<Icon name="spinner" lineHeight="64px" size={3} spin color="gray" />
);
return (
<Box m={1} p={0} inline width="64px">
<Button
p={0}
height="64px"
width="64px"
tooltip={item.name}
tooltipPosition="bottom"
textAlign="right"
disabled={item.amount < 1}
>
<Box
position="absolute"
right="2px"
// in case you click on this instead, let it work as a regular click.
onClick={() =>
act('Release', {
path: item.path,
amount: itemCount,
})
}
>
x{item.amount}
</Box>
<DmIcon
fallback={fallback}
icon={item.icon}
icon_state={item.icon_state}
height="64px"
width="64px"
onClick={() =>
act('Release', {
path: item.path,
amount: itemCount,
})
}
/>
{item.amount > 1 && (
<Box
color="transparent"
minWidth="24px"
height="24px"
lineHeight="24px"
textAlign="center"
position="absolute"
left="0"
bottom="0"
fontWeight="bold"
fontSize="14px"
>
<NumberInput
width="25px"
minValue={1}
maxValue={item.amount}
step={1}
value={itemCount}
onChange={(value) => setItemCount(value)}
/>
</Box>
)}
</Button>
<Box
style={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
textAlign: 'center',
}}
>
{item.name}
</Box>
</Box>
<ImageButton
key={item.path}
dmIcon={item.icon}
dmIconState={item.icon_state}
disabled={item.amount < 1}
tooltip={item.name}
tooltipPosition={'bottom'}
buttons={
item.amount > 1 && (
<NumberInput
minValue={1}
maxValue={item.amount}
step={1}
value={1}
onChange={(value) => {
act('Release', {
path: item.path,
amount: value,
});
}}
/>
)
}
buttonsAlt={
<Stack bold color="rgb(185, 185, 185)" fontSize={0.8}>
<Stack.Item grow />
<Stack.Item style={{ textShadow: '0 1px 1px black' }}>
x{item.amount}
</Stack.Item>
</Stack>
}
onClick={() =>
act('Release', {
path: item.path,
amount: 1,
})
}
onRightClick={() =>
act('Release', {
path: item.path,
amount: item.amount,
})
}
>
{item.name}
</ImageButton>
) as any;
};
const ItemList = ({ item }) => {
const { act } = useBackend<Data>();
const fallback = (
<Icon name="spinner" lineHeight="32px" size={3} spin color="gray" />
);
const [itemCount, setItemCount] = useState(1);
const disabled = item.amount <= 1;
return (
<Stack>
<Stack.Item>
<Button
p={0}
m={0}
color="transparent"
width="32px"
height="32px"
onClick={() =>
act('Release', {
path: item.path,
amount: 1,
})
}
<ImageButton
key={item.path}
fluid
imageSize={32}
dmIcon={item.icon}
dmIconState={item.icon_state}
disabled={item.amount < 1}
buttons={
<Stack
opacity={disabled && 0.5}
backgroundColor={'rgba(175, 175, 175, 0.1)'}
style={{ pointerEvents: disabled ? 'none' : 'auto' }}
>
<DmIcon
fallback={fallback}
icon={item.icon}
icon_state={item.icon_state}
<NumberInput
width="40px"
minValue={1}
maxValue={item.amount}
step={1}
value={1}
onChange={(value) => {
act('Release', {
path: item.path,
amount: value,
});
}}
/>
</Button>
</Stack.Item>
<Stack.Item
style={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
lineHeight: '32px',
}}
>
{item.name}
</Stack.Item>
<Stack.Item
grow
style={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
lineHeight: '32px',
}}
>
<Box
style={{
marginTop: '20px',
borderBottom: '1px dotted gray',
}}
/>
</Stack.Item>
{item.amount > 1 && (
<Stack.Item
style={{
lineHeight: '32px',
}}
>
{`x${item.amount}`}
</Stack>
}
onClick={() =>
act('Release', {
path: item.path,
amount: 1,
})
}
onRightClick={() =>
act('Release', {
path: item.path,
amount: item.amount,
})
}
>
<Stack textAlign="left">
<Stack.Item grow>{item.name}</Stack.Item>
<Stack.Item opacity={0.5} fontSize={0.8}>
x{item.amount}
</Stack.Item>
)}
<Stack.Item>
<Button
py="4px"
mt="4px"
height="24px"
lineHeight="16px"
onClick={() =>
act('Release', {
path: item.path,
amount: itemCount,
})
}
>
Vend
</Button>
<NumberInput
width="25px"
minValue={1}
maxValue={item.amount}
step={1}
value={itemCount}
onChange={(value) => setItemCount(value)}
/>
</Stack.Item>
</Stack>
</Stack>
</ImageButton>
) as any;
};
+1 -1
View File
@@ -18,7 +18,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-popper": "^2.3.0",
"tgui-core": "^1.7.2",
"tgui-core": "^1.7.4",
"tgui-dev-server": "workspace:*",
"tgui-polyfill": "workspace:*"
}
+8 -8
View File
@@ -18723,17 +18723,17 @@ __metadata:
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
tgui: "workspace:*"
tgui-core: "npm:^1.7.2"
tgui-core: "npm:^1.7.4"
languageName: unknown
linkType: soft
"tgui-core@npm:^1.7.2":
version: 1.7.2
resolution: "tgui-core@npm:1.7.2"
"tgui-core@npm:^1.7.4":
version: 1.7.4
resolution: "tgui-core@npm:1.7.4"
peerDependencies:
react: ^18.2.0
react-dom: ^18.2.0
checksum: 10c0/401102042d4d0ede4f8658160124cd523c4ffc44492e0f7d0500d25e0b8a085be2b6f829c89b81eb706c369af16d6d57375704337d7bcc813480caa749c258d4
checksum: 10c0/06b9d8702d6ae170383b28725bf6308cd320499367ea8ea1f9dd5039ab371cfe34419a7d3d1e0e2b86884326e8d4ab116e5601208d2f85d215b7c464fd32fdbf
languageName: node
linkType: hard
@@ -18761,7 +18761,7 @@ __metadata:
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
tgui: "workspace:*"
tgui-core: "npm:^1.7.2"
tgui-core: "npm:^1.7.4"
tgui-dev-server: "workspace:*"
tgui-polyfill: "workspace:*"
languageName: unknown
@@ -18788,7 +18788,7 @@ __metadata:
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
tgui: "workspace:*"
tgui-core: "npm:^1.7.2"
tgui-core: "npm:^1.7.4"
tgui-polyfill: "workspace:*"
languageName: unknown
linkType: soft
@@ -18850,7 +18850,7 @@ __metadata:
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
react-popper: "npm:^2.3.0"
tgui-core: "npm:^1.7.2"
tgui-core: "npm:^1.7.4"
tgui-dev-server: "workspace:*"
tgui-polyfill: "workspace:*"
languageName: unknown