Skip to content

Commit

Permalink
[MIRROR] Typescript image component [MDB IGNORE] (#25699) (#1188)
Browse files Browse the repository at this point in the history
* Typescript image component (#80291)

No longer rely on Box to create images. A pretty simple change for most
UIs. Solves a kinda weird solution for image rendering we had in Box.

Did some small refactors along the way. Emojipedia is in TS now.
Bug fix
Fixes #80282
🆑
fix: Emojipedia shouldn't bluescreen anymore.
/🆑

* Typescript image component

---------

Co-authored-by: SkyratBot <[email protected]>
Co-authored-by: Jeremiah <[email protected]>
  • Loading branch information
3 people authored Dec 18, 2023
1 parent 3fb55e2 commit bca6148
Show file tree
Hide file tree
Showing 25 changed files with 237 additions and 136 deletions.
11 changes: 4 additions & 7 deletions tgui/packages/tgui/components/BodyZoneSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, createRef } from 'react';
import { resolveAsset } from '../assets';
import { Box } from './Box';
import { Image } from './Image';

export enum BodyZone {
Head = 'head',
Expand Down Expand Up @@ -86,8 +86,7 @@ export class BodyZoneSelector extends Component<
position: 'relative',
}}
>
<Box
as="img"
<Image
src={resolveAsset(`body_zones.base_${theme}.png`)}
onClick={() => {
const onClick = this.props.onClick;
Expand Down Expand Up @@ -120,8 +119,7 @@ export class BodyZoneSelector extends Component<
/>

{selectedZone && (
<Box
as="img"
<Image
src={resolveAsset(`body_zones.${selectedZone}.png`)}
style={{
pointerEvents: 'none',
Expand All @@ -133,8 +131,7 @@ export class BodyZoneSelector extends Component<
)}

{hoverZone && hoverZone !== selectedZone && (
<Box
as="img"
<Image
src={resolveAsset(`body_zones.${hoverZone}.png`)}
style={{
opacity: 0.5,
Expand Down
5 changes: 4 additions & 1 deletion tgui/packages/tgui/components/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { BooleanLike, classes } from 'common/react';
import { createElement, ReactNode } from 'react';
import { CSS_COLORS } from '../constants';
import { logger } from '../logging';

export type BoxProps = {
[key: string]: any;
Expand Down Expand Up @@ -252,7 +253,9 @@ export const Box = (props: BoxProps) => {
const computedProps = computeBoxProps(rest);

if (as === 'img') {
computedProps.style['-ms-interpolation-mode'] = 'nearest-neighbor';
logger.error(
'Box component cannot be used as an image. Use Image component instead.',
);
}

// Render the component
Expand Down
48 changes: 48 additions & 0 deletions tgui/packages/tgui/components/Image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ReactNode } from 'react';
import { BoxProps, computeBoxProps } from './Box';
import { Tooltip } from './Tooltip';

type Props = Partial<{
fixBlur: boolean; // true is default, this is an ie thing
objectFit: 'contain' | 'cover'; // fill is default
tooltip: ReactNode;
}> &
IconUnion &
BoxProps;

// at least one of these is required
type IconUnion =
| {
className?: string;
src: string;
}
| {
className: string;
src?: string;
};

/** Image component. Use this instead of Box as="img". */
export const Image = (props: Props) => {
const {
className,
fixBlur = true,
objectFit = 'fill',
src,
tooltip,
...rest
} = props;

const computedStyle = {
...computeBoxProps(rest).style,
'-ms-interpolation-mode': fixBlur ? 'nearest-neighbor' : 'auto',
objectFit,
};

let content = <img className={className} src={src} style={computedStyle} />;

if (tooltip) {
content = <Tooltip content={tooltip}>{content}</Tooltip>;
}

return content;
};
1 change: 1 addition & 0 deletions tgui/packages/tgui/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export { Flex } from './Flex';
export { FitText } from './FitText';
export { Grid } from './Grid';
export { Icon } from './Icon';
export { Image } from './Image';
export { InfinitePlane } from './InfinitePlane';
export { Input } from './Input';
export { KeyListener } from './KeyListener';
Expand Down
9 changes: 3 additions & 6 deletions tgui/packages/tgui/interfaces/DestructiveAnalyzer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Box, Section, NoticeBox } from '../components';
import { Button, Box, Section, NoticeBox, Image } from '../components';
import { Window } from '../layouts';

type Data = {
Expand Down Expand Up @@ -68,14 +68,11 @@ export const DestructiveAnalyzer = (props) => {
/>
}
>
<Box
as="img"
<Image
src={`data:image/jpeg;base64,${item_icon}`}
height="64px"
width="64px"
style={{
verticalAlign: 'middle',
}}
verticalAlign="middle"
/>
</Section>
<Section title="Deconstruction Methods">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { classes } from 'common/react';
import { resolveAsset } from '../../assets';
import { useBackend } from '../../backend';
import { Box, Button, Section, Stack } from '../../components';
import { Box, Button, Section, Stack, Image } from '../../components';
import { MutationInfo } from './MutationInfo';
import {
CLEAR_GENE,
Expand All @@ -20,8 +20,7 @@ const GenomeImage = (props) => {
outline = '2px solid #22aa00';
}
return (
<Box
as="img"
<Image
src={url}
style={{
width: '64px',
Expand Down
6 changes: 3 additions & 3 deletions tgui/packages/tgui/interfaces/ExodroneConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Button,
Dimmer,
Icon,
Image,
LabeledList,
Modal,
ProgressBar,
Expand Down Expand Up @@ -738,8 +739,7 @@ const EventScreen = (props: { drone: DroneData; event: FullEventData }) => {
<Stack.Item>
<Stack fill>
<Stack.Item>
<Box
as="img"
<Image
src={resolveAsset(event.image)}
height="125px"
width="250px"
Expand Down Expand Up @@ -808,7 +808,7 @@ export const AdventureScreen = (props: {
</Stack.Item>
<Stack.Divider />
<Stack.Item>
<Box as="img" src={imgSource} height="100px" width="200px" />
<Image src={imgSource} height="100px" width="200px" />
<Stack vertical>
<Stack.Divider />
<Stack.Item grow />
Expand Down
69 changes: 34 additions & 35 deletions tgui/packages/tgui/interfaces/FishingRod.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Box, Stack, Button, Section, Flex } from '../components';
import { Box, Stack, Button, Section, Flex, Image } from '../components';

type FishingRodData = {
type Data = {
bait_name: string;
bait_icon: string;
line_name: string;
Expand All @@ -12,46 +12,36 @@ type FishingRodData = {
description: string;
};

type FishingSlotProps = {
type Props = {
name: string;
slot: string;
current_item_name: string | null;
current_item_icon: string | null;
};

const FishingRodSlot = (props: FishingSlotProps) => {
const FishingRodSlot = (props: Props) => {
const { act } = useBackend();

const icon_wrapper = (icon) => (
<Box
as="img"
width="64px" // todo come up with some way to scale this sanely
height="64px"
src={`data:image/jpeg;base64,${icon}`}
style={{
verticalAlign: 'middle',
objectFit: 'cover',
}}
/>
);
const { current_item_icon, name, slot, current_item_name } = props;

return (
<Section title={`${props.name}`}>
<Section title={`${name}`}>
<Stack>
<Stack.Item grow>
<Button
fluid
onClick={() => act('slot_action', { slot: props.slot })}
>
<Button fluid onClick={() => act('slot_action', { slot: slot })}>
<Flex>
<Flex.Item>
{!!props.current_item_icon &&
icon_wrapper(props.current_item_icon)}
{!!current_item_icon && (
<Image
width="64px" // todo come up with some way to scale this sanely
height="64px"
src={`data:image/jpeg;base64,${current_item_icon}`}
verticalAlign="middle"
objectFit="cover"
/>
)}
</Flex.Item>
<Flex.Item grow align="center">
<Box textAlign="center">
{props.current_item_name ?? 'None'}
</Box>
<Box textAlign="center">{current_item_name ?? 'None'}</Box>
</Flex.Item>
</Flex>
</Button>
Expand All @@ -62,7 +52,16 @@ const FishingRodSlot = (props: FishingSlotProps) => {
};

export const FishingRod = (props) => {
const { data } = useBackend<FishingRodData>();
const { data } = useBackend<Data>();
const {
bait_name,
bait_icon,
line_name,
line_icon,
hook_name,
hook_icon,
description,
} = data;

return (
<Window height={300} width={300}>
Expand All @@ -71,23 +70,23 @@ export const FishingRod = (props) => {
<FishingRodSlot
name="Bait"
slot="bait"
current_item_name={data.bait_name}
current_item_icon={data.bait_icon}
current_item_name={bait_name}
current_item_icon={bait_icon}
/>
<FishingRodSlot
name="Line"
slot="line"
current_item_name={data.line_name}
current_item_icon={data.line_icon}
current_item_name={line_name}
current_item_icon={line_icon}
/>
<FishingRodSlot
name="Hook"
slot="hook"
current_item_name={data.hook_name}
current_item_icon={data.hook_icon}
current_item_name={hook_name}
current_item_icon={hook_icon}
/>
</Section>
<Section>{data.description}</Section>
<Section>{description}</Section>
</Window.Content>
</Window>
);
Expand Down
18 changes: 3 additions & 15 deletions tgui/packages/tgui/interfaces/GreyscaleModifyMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Section,
Table,
Divider,
Image,
} from '../components';
import { Window } from '../layouts';

Expand Down Expand Up @@ -214,13 +215,7 @@ const PreviewDisplay = (props) => {
</Table.Cell>
{data.sprites?.finished ? (
<Table.Cell>
<Box
as="img"
m={0}
mx="10%"
src={data.sprites.finished}
width="75%"
/>
<Image m={0} mx="10%" src={data.sprites.finished} width="75%" />
</Table.Cell>
) : (
<Table.Cell>
Expand Down Expand Up @@ -271,14 +266,7 @@ const PreviewDisplay = (props) => {

const SingleSprite = (props) => {
const { source } = props;
return (
<Box
as="img"
src={source}
width="100%"
style={{ '-ms-interpolation-mode': 'nearest-neighbor' }}
/>
);
return <Image src={source} />;
};

const LoadingAnimation = () => {
Expand Down
8 changes: 3 additions & 5 deletions tgui/packages/tgui/interfaces/Newscaster.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Box,
Button,
Divider,
Image,
LabeledList,
Modal,
Section,
Expand Down Expand Up @@ -256,10 +257,7 @@ const NewscasterWantedScreen = (props) => {
<Box bold>{activeWanted.criminal}</Box>
<Box italic>{activeWanted.crime}</Box>
</Section>
<Box
as="img"
src={activeWanted.image ? activeWanted.image : null}
/>
<Image src={activeWanted.image ? activeWanted.image : null} />
<Box italic>
Posted by {activeWanted.author ? activeWanted.author : 'N/A'}
</Box>
Expand Down Expand Up @@ -608,7 +606,7 @@ const NewscasterChannelMessages = (props) => {
</Section>
)}
{message.photo !== null && !message.censored_message && (
<Box as="img" src={message.photo} />
<Image src={message.photo} />
)}
{!!message.comments && (
<Box>
Expand Down
4 changes: 2 additions & 2 deletions tgui/packages/tgui/interfaces/NtosCamera.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useBackend } from '../backend';
import { NtosWindow } from '../layouts';
import { Button, Box, NoticeBox, Stack } from '../components';
import { Button, Image, NoticeBox, Stack } from '../components';

export const NtosCamera = (props) => {
return (
Expand Down Expand Up @@ -35,7 +35,7 @@ export const NtosCameraContent = (props) => {
/>
</Stack.Item>
<Stack.Item>
<Box as="img" src={photo} />
<Image src={photo} />
</Stack.Item>
</Stack>
);
Expand Down
Loading

0 comments on commit bca6148

Please sign in to comment.