From e4e5c30322d85cff4b0faadbb673e138e55816c0 Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:27:42 -0400 Subject: [PATCH] [MIRROR] Fixes tgui alert buttons (#2064) * Fixes tgui alert buttons (#82714) ## 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
before/after 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)
## Why It's Good For The Game Looks better, runs smoother, no clipping ## Changelog :cl: fix: TGUI Alerts shouldn't have such wonky buttons any more /:cl: --------- Co-authored-by: lessthanthree <83487515+lessthnthree@users.noreply.github.com> * Fixes tgui alert buttons --------- Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: lessthanthree <83487515+lessthnthree@users.noreply.github.com> --- tgui/packages/tgui/components/Stack.tsx | 30 ++- tgui/packages/tgui/interfaces/AlertModal.tsx | 224 ++++++++++-------- .../tgui/styles/components/Stack.scss | 18 ++ 3 files changed, 159 insertions(+), 113 deletions(-) diff --git a/tgui/packages/tgui/components/Stack.tsx b/tgui/packages/tgui/components/Stack.tsx index 18ae34c09e8..3f5cf72123c 100644 --- a/tgui/packages/tgui/components/Stack.tsx +++ b/tgui/packages/tgui/components/Stack.tsx @@ -17,14 +17,23 @@ 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 (
{ 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; }>; -const StackItem = (props: StackItemProps) => { +function StackItem(props: StackItemProps) { const { className, innerRef, ...rest } = props; + return (
{ {...computeFlexItemProps(rest)} /> ); -}; +} Stack.Item = StackItem; @@ -70,8 +81,9 @@ type StackDividerProps = FlexItemProps & hidden: boolean; }>; -const StackDivider = (props: StackDividerProps) => { +function StackDivider(props: StackDividerProps) { const { className, hidden, ...rest } = props; + return (
{ {...computeFlexItemProps(rest)} /> ); -}; +} Stack.Divider = StackDivider; diff --git a/tgui/packages/tgui/interfaces/AlertModal.tsx b/tgui/packages/tgui/interfaces/AlertModal.tsx index d5b931dc08c..62b6e8bbbc3 100644 --- a/tgui/packages/tgui/interfaces/AlertModal.tsx +++ b/tgui/packages/tgui/interfaces/AlertModal.tsx @@ -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(); +export function AlertModal(props) { + const { act, data } = useBackend(); const { autofocus, buttons = [], @@ -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) { + 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 ( {!!timeout && } - { - 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); - } - }} - > +
- + {message} - + {!!autofocus && } - + {isVerbose ? ( + + ) : ( + + )}
); +} + +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(); +function HorizontalButtons(props: ButtonDisplayProps) { + const { act, data } = useBackend(); const { buttons = [], large_buttons, swapped_buttons } = data; const { selected } = props; return ( - - {buttons?.map((button, index) => - !!large_buttons && buttons.length < 3 ? ( - - - - ) : ( - - - - ), - )} - + + {buttons.map((button, index) => ( + + + + ))} + ); -}; +} /** - * 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(); - const { large_buttons } = data; - const { button, selected } = props; - const buttonWidth = button.length > 7 ? button.length : 7; +function VerticalButtons(props: ButtonDisplayProps) { + const { act, data } = useBackend(); + const { buttons = [], large_buttons, swapped_buttons } = data; + const { selected } = props; return ( - + {buttons.map((button, index) => ( + + + + ))} + ); -}; +} diff --git a/tgui/packages/tgui/styles/components/Stack.scss b/tgui/packages/tgui/styles/components/Stack.scss index 02d0e8bbe24..3529c700183 100644 --- a/tgui/packages/tgui/styles/components/Stack.scss +++ b/tgui/packages/tgui/styles/components/Stack.scss @@ -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; }