From f54018f41fe1451a98ed1e36cb92ceb6e244fe26 Mon Sep 17 00:00:00 2001 From: Paul Mullen <101871009+mullenpaul@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:35:19 +0000 Subject: [PATCH] Fix bug with RestrictedInput TGUI component (#5651) # About the pull request Should fix bug in restricted input TGUI component. The number was being clamped on every input, which meant that applying a number of 30 into a range of 19-90, 3 would be bounded to 19 and then the 0 would make the value 190, being bounded to 90. This change performs the check on blur. # Explain why it's good for the game Fixes https://github.com/cmss13-devs/cmss13/issues/5610 # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: code: rework logic of restrictedinput component to reduce checks /:cl: --- code/modules/tgui/tgui_number_input.dm | 1 + .../tgui/components/RestrictedInput.jsx | 63 +++++++++++++------ .../tgui/interfaces/NumberInputModal.tsx | 22 +++++-- 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/code/modules/tgui/tgui_number_input.dm b/code/modules/tgui/tgui_number_input.dm index 4c06b5333b6d..356448853db3 100644 --- a/code/modules/tgui/tgui_number_input.dm +++ b/code/modules/tgui/tgui_number_input.dm @@ -145,6 +145,7 @@ "min_value" = min_value, "preferences" = list(), "title" = title, + "integer_only" = integer_only ) /datum/tgui_input_number/ui_data(mob/user) diff --git a/tgui/packages/tgui/components/RestrictedInput.jsx b/tgui/packages/tgui/components/RestrictedInput.jsx index 082fc606d998..f61025039f34 100644 --- a/tgui/packages/tgui/components/RestrictedInput.jsx +++ b/tgui/packages/tgui/components/RestrictedInput.jsx @@ -1,48 +1,56 @@ -import { classes } from 'common/react'; +import { KEY_ENTER, KEY_ESCAPE } from 'common/keycodes'; import { clamp } from 'common/math'; +import { classes } from 'common/react'; import { Component, createRef } from 'react'; + import { Box } from './Box'; -import { KEY_ESCAPE, KEY_ENTER } from 'common/keycodes'; -const DEFAULT_MIN = -16777216; -const DEFAULT_MAX = 16777216; +const DEFAULT_MIN = 0; +const DEFAULT_MAX = 10000; /** - * Takes a string input and parses integers from it. + * Takes a string input and parses integers or floats from it. * If none: Minimum is set. * Else: Clamps it to the given range. */ -const getClampedNumber = (value, minValue, maxValue) => { +const getClampedNumber = (value, minValue, maxValue, allowFloats) => { const minimum = minValue || DEFAULT_MIN; const maximum = maxValue || maxValue === 0 ? maxValue : DEFAULT_MAX; - const defaultValue = maximum < 0 ? minimum : minimum > 0 ? minimum : 0; if (!value || !value.length) { - return String(defaultValue); + return String(minimum); } - let parsedValue = parseFloat(value.replace(/[^\-.\d]/g, ''), 10); + let parsedValue = allowFloats + ? parseFloat(value.replace(/[^\-\d.]/g, '')) + : parseInt(value.replace(/[^\-\d]/g, ''), 10); if (isNaN(parsedValue)) { - return String(defaultValue); + return String(minimum); } else { return String(clamp(parsedValue, minimum, maximum)); } }; export class RestrictedInput extends Component { - constructor() { - super(); + constructor(props) { + super(props); this.inputRef = createRef(); this.state = { editing: false, }; this.handleBlur = (e) => { + const { maxValue, minValue, allowFloats } = this.props; const { editing } = this.state; if (editing) { + e.target.value = getClampedNumber( + e.target.value, + minValue, + maxValue, + allowFloats + ); this.setEditing(false); } }; this.handleChange = (e) => { - const { maxValue, minValue, onChange } = this.props; - e.target.value = getClampedNumber(e.target.value, minValue, maxValue); + const { onChange } = this.props; if (onChange) { onChange(e, +e.target.value); } @@ -64,9 +72,14 @@ export class RestrictedInput extends Component { } }; this.handleKeyDown = (e) => { - const { maxValue, minValue, onChange, onEnter } = this.props; + const { maxValue, minValue, onChange, onEnter, allowFloats } = this.props; if (e.keyCode === KEY_ENTER) { - const safeNum = getClampedNumber(e.target.value, minValue, maxValue); + const safeNum = getClampedNumber( + e.target.value, + minValue, + maxValue, + allowFloats + ); this.setEditing(false); if (onChange) { onChange(e, +safeNum); @@ -91,11 +104,16 @@ export class RestrictedInput extends Component { } componentDidMount() { - const { maxValue, minValue } = this.props; + const { maxValue, minValue, allowFloats } = this.props; const nextValue = this.props.value?.toString(); const input = this.inputRef.current; if (input) { - input.value = getClampedNumber(nextValue, minValue, maxValue); + input.value = getClampedNumber( + nextValue, + minValue, + maxValue, + allowFloats + ); } if (this.props.autoFocus || this.props.autoSelect) { setTimeout(() => { @@ -109,14 +127,19 @@ export class RestrictedInput extends Component { } componentDidUpdate(prevProps, _) { - const { maxValue, minValue } = this.props; + const { maxValue, minValue, allowFloats } = this.props; const { editing } = this.state; const prevValue = prevProps.value?.toString(); const nextValue = this.props.value?.toString(); const input = this.inputRef.current; if (input && !editing) { if (nextValue !== prevValue && nextValue !== input.value) { - input.value = getClampedNumber(nextValue, minValue, maxValue); + input.value = getClampedNumber( + nextValue, + minValue, + maxValue, + allowFloats + ); } } } diff --git a/tgui/packages/tgui/interfaces/NumberInputModal.tsx b/tgui/packages/tgui/interfaces/NumberInputModal.tsx index eff7acb62a1b..32d7161d8faf 100644 --- a/tgui/packages/tgui/interfaces/NumberInputModal.tsx +++ b/tgui/packages/tgui/interfaces/NumberInputModal.tsx @@ -13,11 +13,19 @@ type NumberInputData = { min_value: number | null; timeout: number; title: string; + integer_only: 0 | 1; }; -export const NumberInputModal = (props) => { +export const NumberInputModal = () => { const { act, data } = useBackend(); - const { init_value, large_buttons, message = '', timeout, title } = data; + const { + init_value, + large_buttons, + message = '', + timeout, + title, + integer_only, + } = data; const [input, setInput] = useLocalState('input', init_value); const onChange = (value: number) => { if (value === input) { @@ -56,7 +64,12 @@ export const NumberInputModal = (props) => { {message} - + @@ -72,7 +85,7 @@ export const NumberInputModal = (props) => { const InputArea = (props) => { const { act, data } = useBackend(); const { min_value, max_value, init_value } = data; - const { input, onClick, onChange } = props; + const { input, onClick, onChange, integer_only } = props; return ( @@ -94,6 +107,7 @@ const InputArea = (props) => { onChange={(_, value) => onChange(value)} onEnter={(_, value) => act('submit', { entry: value })} value={input} + allowFloats={integer_only === 1 ? false : true} />