Skip to content

Commit

Permalink
[MISSED MIRROR] Input changes take XII: Responsive small lists (#8072…
Browse files Browse the repository at this point in the history
…0) (#346)

Input changes take XII: Responsive small lists (#80720)

## About The Pull Request
Admittedly it's been tough to find a sweet spot that will let us handle
large lists and basic inputs. This PR aims to address this by adding the
`expensive` prop on Input. This introduces the debounce that #80688
without compromising the responsiveness of smaller filters like those in
orbit, giving the author the keys to monitor performance of their UIs.
On a more human note inputs just feel better with this PR.

Along with this change I've expanded documentation for the typescript
types and included a discriminating union to let contributors know that
the `expensive` prop requires `onInput` to work.
## Why It's Good For The Game
Bug fixes, responsiveness, etc
## Changelog
:cl:
fix: Search bars for smaller lists should return to their former
responsiveness.
/:cl:

Co-authored-by: Jeremiah <[email protected]>
  • Loading branch information
2 people authored and FFMirrorBot committed Jan 6, 2024
1 parent bd0f3ed commit f45305b
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
2 changes: 2 additions & 0 deletions tgui/docs/component-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ A basic text input, which allow users to enter text into a UI.
- `onEnter: (e, value) => void` - Fires when the user hits enter.
- `onEscape: (e) => void` - Fires when the user hits escape.
- `onInput: (e, value) => void` - Fires when the user types into the input.
- `expensive: boolean` - Introduces a delay before updating the input. Useful for large filters,
where you don't want to update on every keystroke.

### `Knob`

Expand Down
84 changes: 68 additions & 16 deletions tgui/packages/tgui/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,81 @@ import { KeyboardEvent, SyntheticEvent, useEffect, useRef } from 'react';

import { Box, BoxProps } from './Box';

type Props = Partial<{
type ConditionalProps =
| {
/**
* Mark this if you want to debounce onInput.
*
* This is useful for expensive filters, large lists etc.
*
* Requires `onInput` to be set.
*/
expensive?: boolean;
/**
* Fires on each key press / value change. Used for searching.
*
* If it's a large list, consider using `expensive` prop.
*/
onInput: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
}
| {
/** This prop requires onInput to be set */
expensive?: never;
onInput?: never;
};

type OptionalProps = Partial<{
/** Automatically focuses the input on mount */
autoFocus: boolean;
/** Automatically selects the input value on focus */
autoSelect: boolean;
/** The class name of the input */
className: string;
/** Disables the input */
disabled: boolean;
/** Mark this if you want the input to be as wide as possible */
fluid: boolean;
/** The maximum length of the input value */
maxLength: number;
/** Mark this if you want to use a monospace font */
monospace: boolean;
/** Fires when user is 'done typing': Clicked out, blur, enter key */
onChange: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
/** Fires once the enter key is pressed */
onEnter: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
onEnter?: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
/** Fires once the escape key is pressed */
onEscape: (event: SyntheticEvent<HTMLInputElement>) => void;
/** Fires on each key press / value change. Used for searching */
onInput: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
/** The placeholder text when everything is cleared */
placeholder: string;
/** Clears the input value on enter */
selfClear: boolean;
/** The state variable of the input. */
value: string | number;
}> &
BoxProps;
}>;

type Props = OptionalProps & ConditionalProps & BoxProps;

export const toInputValue = (value: string | number | undefined) =>
typeof value !== 'number' && typeof value !== 'string' ? '' : String(value);
export function toInputValue(value: string | number | undefined) {
return typeof value !== 'number' && typeof value !== 'string'
? ''
: String(value);
}

const inputDebounce = debounce((onInput: () => void) => onInput(), 200);
const inputDebounce = debounce((onInput: () => void) => onInput(), 250);

export const Input = (props: Props) => {
/**
* ### Input
* A basic text input which allow users to enter text into a UI.
* > Input does not support custom font size and height due to the way
* > it's implemented in CSS. Eventually, this needs to be fixed.
*/
export function Input(props: Props) {
const {
autoFocus,
autoSelect,
className,
disabled,
expensive,
fluid,
maxLength,
monospace,
Expand All @@ -59,7 +101,19 @@ export const Input = (props: Props) => {

const inputRef = useRef<HTMLInputElement>(null);

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
function handleInput(event: SyntheticEvent<HTMLInputElement>) {
if (!onInput) return;

const value = event.currentTarget?.value;

if (expensive) {
inputDebounce(() => onInput(event, value));
} else {
onInput(event, value);
}
}

function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {
if (event.key === KEY.Enter) {
onEnter?.(event, event.currentTarget.value);
if (selfClear) {
Expand All @@ -78,7 +132,7 @@ export const Input = (props: Props) => {
event.currentTarget.value = toInputValue(value);
event.currentTarget.blur();
}
};
}

/** Focuses the input on mount */
useEffect(() => {
Expand Down Expand Up @@ -123,13 +177,11 @@ export const Input = (props: Props) => {
disabled={disabled}
maxLength={maxLength}
onBlur={(event) => onChange?.(event, event.target.value)}
onChange={(event) =>
onInput && inputDebounce(() => onInput(event, event.target.value))
}
onChange={handleInput}
onKeyDown={handleKeyDown}
placeholder={placeholder}
ref={inputRef}
/>
</Box>
);
};
}
2 changes: 2 additions & 0 deletions tgui/packages/tgui/interfaces/CameraConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@ const CameraSelector = (props) => {
<Stack.Item>
<Input
autoFocus
expensive
fluid
mt={1}
placeholder="Search for a camera"
onInput={(e, value) => setSearchText(value)}
value={searchText}
/>
</Stack.Item>
<Stack.Item grow>
Expand Down
1 change: 1 addition & 0 deletions tgui/packages/tgui/interfaces/common/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function SearchBar(props: Props) {
<Stack.Item grow>
<Input
autoFocus={autoFocus}
expensive
fluid
onInput={(e, value) => onSearch(value)}
placeholder={placeholder}
Expand Down

0 comments on commit f45305b

Please sign in to comment.