Skip to content

Commit

Permalink
[MIRROR] Fixes tgui alert buttons (#2064) (#2950)
Browse files Browse the repository at this point in the history
* Fixes tgui alert buttons (#82714)

<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request
Been on my mind for a long time
I did this while relatively inexperienced (how do I center a div etc)

Fixes an issue inherent to our stack implementation where reversing the
direction caused strange spacing issues. This effectively reverses the
extra margins in css so it looks identically spaced whether reversed/not

<details>
<summary>before/after</summary>

Before (normal buttons)

![image](https://github.com/tgstation/tgstation/assets/42397676/a91f5522-b363-4069-9e71-9852b15e9d98)

Before (large buttons)

![image](https://github.com/tgstation/tgstation/assets/42397676/1448b6db-9417-4f93-bbe3-3cb39c70d6a4)

After (normal buttons)
![Screenshot 2024-04-16
234002](https://github.com/tgstation/tgstation/assets/42397676/e19f6a71-bd11-4f21-8cfc-b29e89020f5c)

After (large buttons)
![Screenshot 2024-04-16
234015](https://github.com/tgstation/tgstation/assets/42397676/6adc55cc-42ca-4c1a-a18c-c2dadf3413ce)

Long buttons (normal)
![Screenshot 2024-04-17
174208](https://github.com/tgstation/tgstation/assets/42397676/b919c049-658c-4b9e-ab3f-6d06eb9d467e)

Long buttons (large buttons (I am 35 and yet still fight the forces of
chaos))
![Screenshot 2024-04-17
174202](https://github.com/tgstation/tgstation/assets/42397676/01fae7ff-8c51-4789-88ac-9d533e1f0eeb)
</details>

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

## Why It's Good For The Game
Looks better, runs smoother, no clipping
<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

## Changelog

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and it's effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->

:cl:
fix: TGUI Alerts shouldn't have such wonky buttons any more
/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------



* Fixes tgui alert buttons

---------

Co-authored-by: NovaBot <[email protected]>
Co-authored-by: Jeremiah <[email protected]>
Co-authored-by: lessthanthree <[email protected]>
  • Loading branch information
4 people authored Apr 19, 2024
1 parent 93492b8 commit 4de9c8b
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 113 deletions.
30 changes: 21 additions & 9 deletions tgui/packages/tgui/components/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,50 @@ import {
} from './Flex';

type Props = Partial<{
vertical: boolean;
/** Fills available space. */
fill: boolean;
/** Reverses the stack. */
reverse: boolean;
/** Flex column */
vertical: boolean;
/** Adds zebra striping to the stack. */
zebra: boolean;
}> &
FlexProps;

export const Stack = (props: Props) => {
const { className, vertical, fill, zebra, ...rest } = props;
export function Stack(props: Props) {
const { className, vertical, fill, reverse, zebra, ...rest } = props;

const directionPrefix = vertical ? 'column' : 'row';
const directionSuffix = reverse ? '-reverse' : '';

return (
<div
className={classes([
'Stack',
fill && 'Stack--fill',
vertical ? 'Stack--vertical' : 'Stack--horizontal',
zebra && 'Stack--zebra',
reverse && `Stack--reverse${vertical ? '--vertical' : ''}`,
className,
computeFlexClassName(props),
])}
{...computeFlexProps({
direction: vertical ? 'column' : 'row',
direction: `${directionPrefix}${directionSuffix}`,
...rest,
})}
/>
);
};
}

type StackItemProps = FlexItemProps &
Partial<{
innerRef: RefObject<HTMLDivElement>;
}>;

const StackItem = (props: StackItemProps) => {
function StackItem(props: StackItemProps) {
const { className, innerRef, ...rest } = props;

return (
<div
className={classes([
Expand All @@ -61,7 +72,7 @@ const StackItem = (props: StackItemProps) => {
{...computeFlexItemProps(rest)}
/>
);
};
}

Stack.Item = StackItem;

Expand All @@ -70,8 +81,9 @@ type StackDividerProps = FlexItemProps &
hidden: boolean;
}>;

const StackDivider = (props: StackDividerProps) => {
function StackDivider(props: StackDividerProps) {
const { className, hidden, ...rest } = props;

return (
<div
className={classes([
Expand All @@ -84,6 +96,6 @@ const StackDivider = (props: StackDividerProps) => {
{...computeFlexItemProps(rest)}
/>
);
};
}

Stack.Divider = StackDivider;
224 changes: 120 additions & 104 deletions tgui/packages/tgui/interfaces/AlertModal.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import { useState } from 'react';

import {
KEY_ENTER,
KEY_ESCAPE,
KEY_LEFT,
KEY_RIGHT,
KEY_SPACE,
KEY_TAB,
} from '../../common/keycodes';
import { KEY } from 'common/keys';
import { BooleanLike } from 'common/react';
import { KeyboardEvent, useState } from 'react';

import { useBackend } from '../backend';
import { Autofocus, Box, Button, Flex, Section, Stack } from '../components';
import { Autofocus, Box, Button, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Loader } from './common/Loader';

type AlertModalData = {
autofocus: boolean;
type Data = {
autofocus: BooleanLike;
buttons: string[];
large_buttons: boolean;
large_buttons: BooleanLike;
message: string;
swapped_buttons: boolean;
swapped_buttons: BooleanLike;
timeout: number;
title: string;
};

const KEY_DECREMENT = -1;
const KEY_INCREMENT = 1;
enum DIRECTION {
Increment = 1,
Decrement = -1,
}

export const AlertModal = (props) => {
const { act, data } = useBackend<AlertModalData>();
export function AlertModal(props) {
const { act, data } = useBackend<Data>();
const {
autofocus,
buttons = [],
Expand All @@ -36,128 +32,148 @@ export const AlertModal = (props) => {
timeout,
title,
} = data;

const [selected, setSelected] = useState(0);

// At least one of the buttons has a long text message
const isVerbose = buttons.some((button) => button.length > 10);
const largeSpacing = isVerbose && large_buttons ? 20 : 15;

// Dynamically sets window dimensions
const windowHeight =
115 +
120 +
(isVerbose ? largeSpacing * buttons.length : 0) +
(message.length > 30 ? Math.ceil(message.length / 4) : 0) +
(message.length && large_buttons ? 5 : 0);
const windowWidth = 325 + (buttons.length > 2 ? 55 : 0);
const onKey = (direction: number) => {
if (selected === 0 && direction === KEY_DECREMENT) {
setSelected(buttons.length - 1);
} else if (selected === buttons.length - 1 && direction === KEY_INCREMENT) {
setSelected(0);
} else {
setSelected(selected + direction);

const windowWidth = 345 + (buttons.length > 2 ? 55 : 0);

/** Changes button selection, etc */
function keyDownHandler(event: KeyboardEvent<HTMLDivElement>) {
switch (event.key) {
case KEY.Space:
case KEY.Enter:
act('choose', { choice: buttons[selected] });
return;
case KEY.Escape:
act('cancel');
return;
case KEY.Left:
event.preventDefault();
onKey(DIRECTION.Decrement);
return;
case KEY.Tab:
case KEY.Right:
event.preventDefault();
onKey(DIRECTION.Increment);
return;
}
};
}

/** Manages iterating through the buttons */
function onKey(direction: DIRECTION) {
const newIndex = (selected + direction + buttons.length) % buttons.length;
setSelected(newIndex);
}

return (
<Window height={windowHeight} title={title} width={windowWidth}>
{!!timeout && <Loader value={timeout} />}
<Window.Content
onKeyDown={(e) => {
const keyCode = window.event ? e.which : e.keyCode;
/**
* Simulate a click when pressing space or enter,
* allow keyboard navigation, override tab behavior
*/
if (keyCode === KEY_SPACE || keyCode === KEY_ENTER) {
act('choose', { choice: buttons[selected] });
} else if (keyCode === KEY_ESCAPE) {
act('cancel');
} else if (keyCode === KEY_LEFT) {
e.preventDefault();
onKey(KEY_DECREMENT);
} else if (keyCode === KEY_TAB || keyCode === KEY_RIGHT) {
e.preventDefault();
onKey(KEY_INCREMENT);
}
}}
>
<Window.Content onKeyDown={keyDownHandler}>
<Section fill>
<Stack fill vertical>
<Stack.Item grow m={1}>
<Stack.Item m={1}>
<Box color="label" overflow="hidden">
{message}
</Box>
</Stack.Item>
<Stack.Item>
<Stack.Item grow>
{!!autofocus && <Autofocus />}
<ButtonDisplay selected={selected} />
{isVerbose ? (
<VerticalButtons selected={selected} />
) : (
<HorizontalButtons selected={selected} />
)}
</Stack.Item>
</Stack>
</Section>
</Window.Content>
</Window>
);
}

type ButtonDisplayProps = {
selected: number;
};

/**
* Displays a list of buttons ordered by user prefs.
* Technically this handles more than 2 buttons, but you
* should just be using a list input in that case.
*/
const ButtonDisplay = (props) => {
const { data } = useBackend<AlertModalData>();
function HorizontalButtons(props: ButtonDisplayProps) {
const { act, data } = useBackend<Data>();
const { buttons = [], large_buttons, swapped_buttons } = data;
const { selected } = props;

return (
<Flex
align="center"
direction={!swapped_buttons ? 'row-reverse' : 'row'}
fill
justify="space-around"
wrap
>
{buttons?.map((button, index) =>
!!large_buttons && buttons.length < 3 ? (
<Flex.Item grow key={index}>
<AlertButton
button={button}
id={index.toString()}
selected={selected === index}
/>
</Flex.Item>
) : (
<Flex.Item key={index}>
<AlertButton
button={button}
id={index.toString()}
selected={selected === index}
/>
</Flex.Item>
),
)}
</Flex>
<Stack fill justify="space-around" reverse={!swapped_buttons}>
{buttons.map((button, index) => (
<Stack.Item grow={large_buttons ? 1 : undefined} key={index}>
<Button
fluid={!!large_buttons}
minWidth={5}
onClick={() => act('choose', { choice: button })}
overflowX="hidden"
px={2}
py={large_buttons ? 0.5 : 0}
selected={selected === index}
textAlign="center"
>
{!large_buttons ? button : button.toUpperCase()}
</Button>
</Stack.Item>
))}
</Stack>
);
};
}

/**
* Displays a button with variable sizing.
* Technically the parent handles more than 2 buttons, but you
* should just be using a list input in that case.
*/
const AlertButton = (props) => {
const { act, data } = useBackend<AlertModalData>();
const { large_buttons } = data;
const { button, selected } = props;
const buttonWidth = button.length > 7 ? button.length : 7;
function VerticalButtons(props: ButtonDisplayProps) {
const { act, data } = useBackend<Data>();
const { buttons = [], large_buttons, swapped_buttons } = data;
const { selected } = props;

return (
<Button
fluid={!!large_buttons}
height={!!large_buttons && 2}
onClick={() => act('choose', { choice: button })}
m={0.5}
pl={2}
pr={2}
pt={large_buttons ? 0.33 : 0}
selected={selected}
textAlign="center"
width={!large_buttons && buttonWidth}
<Stack
align="center"
fill
justify="space-around"
reverse={!swapped_buttons}
vertical
>
{!large_buttons ? button : button.toUpperCase()}
</Button>
{buttons.map((button, index) => (
<Stack.Item
grow
width={large_buttons ? '100%' : undefined}
key={index}
m={0}
>
<Button
fluid
minWidth={20}
onClick={() => act('choose', { choice: button })}
overflowX="hidden"
px={2}
py={large_buttons ? 0.5 : 0}
selected={selected === index}
textAlign="center"
>
{!large_buttons ? button : button.toUpperCase()}
</Button>
</Stack.Item>
))}
</Stack>
);
};
}
18 changes: 18 additions & 0 deletions tgui/packages/tgui/styles/components/Stack.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ $zebra-background-color: base.$color-bg-section !default;
}
}

.Stack--reverse > .Stack__item {
margin-left: 0;
margin-right: 0.5em;

&:first-child {
margin-right: 0;
}
}

.Stack--reverse--vertical > .Stack__item {
margin-top: 0;
margin-bottom: 0.5em;

&:first-child {
margin-bottom: 0;
}
}

.Stack--zebra > .Stack__item:nth-child(even) {
background-color: $zebra-background-color;
}
Expand Down

0 comments on commit 4de9c8b

Please sign in to comment.