mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-21 03:56:47 +01:00
Port tgui_alert and tgui_input_list
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Creates a TGUI alert window and returns the user's response.
|
||||
*
|
||||
* This proc should be used to create alerts that the caller will wait for a response from.
|
||||
* Arguments:
|
||||
* * user - The user to show the alert to.
|
||||
* * message - The content of the alert, shown in the body of the TGUI window.
|
||||
* * title - The of the alert modal, shown on the top of the TGUI window.
|
||||
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
|
||||
* * timeout - The timeout of the alert, after which the modal will close and qdel itself. Set to zero for no timeout.
|
||||
*/
|
||||
/proc/tgui_alert(mob/user, message = null, title = null, list/buttons = list("Ok"), timeout = 0)
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_modal/alert = new(user, message, title, buttons, timeout)
|
||||
alert.tgui_interact(user)
|
||||
alert.wait()
|
||||
if (alert)
|
||||
. = alert.choice
|
||||
qdel(alert)
|
||||
|
||||
/**
|
||||
* Creates an asynchronous TGUI alert window with an associated callback.
|
||||
*
|
||||
* This proc should be used to create alerts that invoke a callback with the user's chosen option.
|
||||
* Arguments:
|
||||
* * user - The user to show the alert to.
|
||||
* * message - The content of the alert, shown in the body of the TGUI window.
|
||||
* * title - The of the alert modal, shown on the top of the TGUI window.
|
||||
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
|
||||
* * callback - The callback to be invoked when a choice is made.
|
||||
* * timeout - The timeout of the alert, after which the modal will close and qdel itself. Disabled by default, can be set to seconds otherwise.
|
||||
*/
|
||||
/proc/tgui_alert_async(mob/user, message = null, title = null, list/buttons = list("Ok"), datum/callback/callback, timeout = 0)
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_modal/async/alert = new(user, message, title, buttons, callback, timeout)
|
||||
alert.tgui_interact(user)
|
||||
|
||||
/**
|
||||
* # tgui_modal
|
||||
*
|
||||
* Datum used for instantiating and using a TGUI-controlled modal that prompts the user with
|
||||
* a message and has buttons for responses.
|
||||
*/
|
||||
/datum/tgui_modal
|
||||
/// The title of the TGUI window
|
||||
var/title
|
||||
/// The textual body of the TGUI window
|
||||
var/message
|
||||
/// The list of buttons (responses) provided on the TGUI window
|
||||
var/list/buttons
|
||||
/// The button that the user has pressed, null if no selection has been made
|
||||
var/choice
|
||||
/// The time at which the tgui_modal was created, for displaying timeout progress.
|
||||
var/start_time
|
||||
/// The lifespan of the tgui_modal, after which the window will close and delete itself.
|
||||
var/timeout
|
||||
/// Boolean field describing if the tgui_modal was closed by the user.
|
||||
var/closed
|
||||
|
||||
/datum/tgui_modal/New(mob/user, message, title, list/buttons, timeout)
|
||||
src.title = title
|
||||
src.message = message
|
||||
src.buttons = buttons.Copy()
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_modal/Destroy(force, ...)
|
||||
SStgui.close_uis(src)
|
||||
QDEL_NULL(buttons)
|
||||
. = ..()
|
||||
|
||||
/**
|
||||
* Waits for a user's response to the tgui_modal's prompt before returning. Returns early if
|
||||
* the window was closed by the user.
|
||||
*/
|
||||
/datum/tgui_modal/proc/wait()
|
||||
while (!choice && !closed && !QDELETED(src))
|
||||
stoplag(1)
|
||||
|
||||
/datum/tgui_modal/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "AlertModal")
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_modal/tgui_close(mob/user)
|
||||
. = ..()
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_modal/tgui_state(mob/user)
|
||||
return GLOB.tgui_always_state
|
||||
|
||||
/datum/tgui_modal/tgui_data(mob/user)
|
||||
. = list(
|
||||
"title" = title,
|
||||
"message" = message,
|
||||
"buttons" = buttons
|
||||
)
|
||||
|
||||
if(timeout)
|
||||
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
|
||||
/datum/tgui_modal/tgui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
switch(action)
|
||||
if("choose")
|
||||
if (!(params["choice"] in buttons))
|
||||
return
|
||||
choice = params["choice"]
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* # async tgui_modal
|
||||
*
|
||||
* An asynchronous version of tgui_modal to be used with callbacks instead of waiting on user responses.
|
||||
*/
|
||||
/datum/tgui_modal/async
|
||||
/// The callback to be invoked by the tgui_modal upon having a choice made.
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/tgui_modal/async/New(mob/user, message, title, list/buttons, callback, timeout)
|
||||
..(user, title, message, buttons, timeout)
|
||||
src.callback = callback
|
||||
|
||||
/datum/tgui_modal/async/Destroy(force, ...)
|
||||
QDEL_NULL(callback)
|
||||
. = ..()
|
||||
|
||||
/datum/tgui_modal/async/tgui_close(mob/user)
|
||||
. = ..()
|
||||
qdel(src)
|
||||
|
||||
/datum/tgui_modal/async/tgui_act(action, list/params)
|
||||
. = ..()
|
||||
if (!. || choice == null)
|
||||
return
|
||||
callback.InvokeAsync(choice)
|
||||
qdel(src)
|
||||
|
||||
/datum/tgui_modal/async/wait()
|
||||
return
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Creates a TGUI input list window and returns the user's response.
|
||||
*
|
||||
* This proc should be used to create alerts that the caller will wait for a response from.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * message - The content of the input box, shown in the body of the TGUI window.
|
||||
* * title - The title of the input box, shown on the top of the TGUI window.
|
||||
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
|
||||
* * timeout - The timeout of the input box, after which the input box will close and qdel itself. Set to zero for no timeout.
|
||||
*/
|
||||
/proc/tgui_input_list(mob/user, message, title, list/buttons, timeout = 0)
|
||||
if (!user)
|
||||
user = usr
|
||||
if(!length(buttons))
|
||||
return
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_list_input/input = new(user, message, title, buttons, timeout)
|
||||
input.tgui_interact(user)
|
||||
input.wait()
|
||||
if (input)
|
||||
. = input.choice
|
||||
qdel(input)
|
||||
|
||||
/**
|
||||
* Creates an asynchronous TGUI input list window with an associated callback.
|
||||
*
|
||||
* This proc should be used to create inputs that invoke a callback with the user's chosen option.
|
||||
* Arguments:
|
||||
* * user - The user to show the input box to.
|
||||
* * message - The content of the input box, shown in the body of the TGUI window.
|
||||
* * title - The title of the input box, shown on the top of the TGUI window.
|
||||
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
|
||||
* * callback - The callback to be invoked when a choice is made.
|
||||
* * timeout - The timeout of the input box, after which the menu will close and qdel itself. Set to zero for no timeout.
|
||||
*/
|
||||
/proc/tgui_input_list_async(mob/user, message, title, list/buttons, datum/callback/callback, timeout = 60 SECONDS)
|
||||
if (!user)
|
||||
user = usr
|
||||
if(!length(buttons))
|
||||
return
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
var/datum/tgui_list_input/async/input = new(user, message, title, buttons, callback, timeout)
|
||||
input.tgui_interact(user)
|
||||
|
||||
/**
|
||||
* # tgui_list_input
|
||||
*
|
||||
* Datum used for instantiating and using a TGUI-controlled list input that prompts the user with
|
||||
* a message and shows a list of selectable options
|
||||
*/
|
||||
/datum/tgui_list_input
|
||||
/// The title of the TGUI window
|
||||
var/title
|
||||
/// The textual body of the TGUI window
|
||||
var/message
|
||||
/// The list of buttons (responses) provided on the TGUI window
|
||||
var/list/buttons
|
||||
/// Buttons (strings specifically) mapped to the actual value (e.g. a mob or a verb)
|
||||
var/list/buttons_map
|
||||
/// The button that the user has pressed, null if no selection has been made
|
||||
var/choice
|
||||
/// The time at which the tgui_list_input was created, for displaying timeout progress.
|
||||
var/start_time
|
||||
/// The lifespan of the tgui_list_input, after which the window will close and delete itself.
|
||||
var/timeout
|
||||
/// Boolean field describing if the tgui_list_input was closed by the user.
|
||||
var/closed
|
||||
|
||||
/datum/tgui_list_input/New(mob/user, message, title, list/buttons, timeout)
|
||||
src.title = title
|
||||
src.message = message
|
||||
src.buttons = list()
|
||||
src.buttons_map = list()
|
||||
var/list/repeat_buttons = list()
|
||||
|
||||
// Gets rid of illegal characters
|
||||
var/static/regex/whitelistedWords = regex(@{"([^\u0020-\u8000]+)"})
|
||||
|
||||
for(var/i in buttons)
|
||||
var/string_key = whitelistedWords.Replace("[i]", "")
|
||||
|
||||
//avoids duplicated keys E.g: when areas have the same name
|
||||
string_key = avoid_assoc_duplicate_keys(string_key, repeat_buttons)
|
||||
|
||||
src.buttons += string_key
|
||||
src.buttons_map[string_key] = i
|
||||
|
||||
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_list_input/Destroy(force, ...)
|
||||
SStgui.close_uis(src)
|
||||
QDEL_NULL(buttons)
|
||||
. = ..()
|
||||
|
||||
/**
|
||||
* Waits for a user's response to the tgui_list_input's prompt before returning. Returns early if
|
||||
* the window was closed by the user.
|
||||
*/
|
||||
/datum/tgui_list_input/proc/wait()
|
||||
while (!choice && !closed)
|
||||
stoplag(1)
|
||||
|
||||
/datum/tgui_list_input/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ListInput")
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_list_input/tgui_close(mob/user)
|
||||
. = ..()
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_list_input/tgui_state(mob/user)
|
||||
return GLOB.tgui_always_state
|
||||
|
||||
/datum/tgui_list_input/tgui_static_data(mob/user)
|
||||
. = list(
|
||||
"title" = title,
|
||||
"message" = message,
|
||||
"buttons" = buttons
|
||||
)
|
||||
|
||||
/datum/tgui_list_input/tgui_data(mob/user)
|
||||
. = list()
|
||||
if(timeout)
|
||||
.["timeout"] = clamp((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS), 0, 1)
|
||||
|
||||
/datum/tgui_list_input/tgui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
switch(action)
|
||||
if("choose")
|
||||
if (!(params["choice"] in buttons))
|
||||
return
|
||||
choice = buttons_map[params["choice"]]
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
if("cancel")
|
||||
SStgui.close_uis(src)
|
||||
closed = TRUE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* # async tgui_list_input
|
||||
*
|
||||
* An asynchronous version of tgui_list_input to be used with callbacks instead of waiting on user responses.
|
||||
*/
|
||||
/datum/tgui_list_input/async
|
||||
/// The callback to be invoked by the tgui_list_input upon having a choice made.
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/tgui_list_input/async/New(mob/user, message, title, list/buttons, callback, timeout)
|
||||
..(user, title, message, buttons, timeout)
|
||||
src.callback = callback
|
||||
|
||||
/datum/tgui_list_input/async/Destroy(force, ...)
|
||||
QDEL_NULL(callback)
|
||||
. = ..()
|
||||
|
||||
/datum/tgui_list_input/async/tgui_close(mob/user)
|
||||
. = ..()
|
||||
qdel(src)
|
||||
|
||||
/datum/tgui_list_input/async/tgui_act(action, list/params)
|
||||
. = ..()
|
||||
if (!. || choice == null)
|
||||
return
|
||||
callback.InvokeAsync(choice)
|
||||
qdel(src)
|
||||
|
||||
/datum/tgui_list_input/async/wait()
|
||||
return
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 bobbahbrown (https://github.com/bobbahbrown)
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { clamp01 } from 'common/math';
|
||||
import { useBackend } from '../backend';
|
||||
import { Component, createRef } from 'inferno';
|
||||
import { Box, Flex, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import {
|
||||
KEY_ENTER,
|
||||
KEY_LEFT,
|
||||
KEY_RIGHT,
|
||||
KEY_SPACE,
|
||||
KEY_TAB,
|
||||
} from 'common/keycodes';
|
||||
|
||||
export class AlertModal extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.buttonRefs = [createRef()];
|
||||
this.state = { current: 0 };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { data } = useBackend(this.context);
|
||||
const { buttons } = data;
|
||||
const { current } = this.state;
|
||||
const button = this.buttonRefs[current].current;
|
||||
|
||||
// Fill ref array with refs for other buttons
|
||||
for (let i = 1; i < buttons.length; i++) {
|
||||
this.buttonRefs.push(createRef());
|
||||
}
|
||||
|
||||
setTimeout(() => button.focus(), 1);
|
||||
}
|
||||
|
||||
setCurrent(current, isArrowKey) {
|
||||
const { data } = useBackend(this.context);
|
||||
const { buttons } = data;
|
||||
|
||||
// Mimic alert() behavior for tabs and arrow keys
|
||||
if (current >= buttons.length) {
|
||||
current = isArrowKey ? current - 1 : 0;
|
||||
} else if (current < 0) {
|
||||
current = isArrowKey ? 0 : buttons.length - 1;
|
||||
}
|
||||
|
||||
const button = this.buttonRefs[current].current;
|
||||
|
||||
// Prevents an error from occurring on close
|
||||
if (button) {
|
||||
setTimeout(() => button.focus(), 1);
|
||||
}
|
||||
this.setState({ current });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { act, data } = useBackend(this.context);
|
||||
const { title, message, buttons, timeout } = data;
|
||||
const { current } = this.state;
|
||||
const focusCurrentButton = () => this.setCurrent(current, false);
|
||||
|
||||
return (
|
||||
<Window
|
||||
title={title}
|
||||
width={350}
|
||||
height={150}
|
||||
canClose={timeout > 0}>
|
||||
{timeout && <Loader value={timeout} />}
|
||||
<Window.Content
|
||||
onFocus={focusCurrentButton}
|
||||
onClick={focusCurrentButton}>
|
||||
<Section fill>
|
||||
<Flex direction="column" height="100%">
|
||||
<Flex.Item grow={1}>
|
||||
<Flex
|
||||
direction="column"
|
||||
className="AlertModal__Message"
|
||||
height="100%">
|
||||
<Flex.Item>
|
||||
<Box m={1}>
|
||||
{message}
|
||||
</Box>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Flex.Item>
|
||||
<Flex.Item my={2}>
|
||||
<Flex className="AlertModal__Buttons">
|
||||
{buttons.map((button, buttonIndex) => (
|
||||
<Flex.Item key={buttonIndex} mx={1}>
|
||||
<div
|
||||
ref={this.buttonRefs[buttonIndex]}
|
||||
className="Button Button--color--default"
|
||||
px={3}
|
||||
onClick={() => act("choose", { choice: button })}
|
||||
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: button });
|
||||
} else if (
|
||||
keyCode === KEY_LEFT
|
||||
|| (e.shiftKey && keyCode === KEY_TAB)
|
||||
) {
|
||||
this.setCurrent(current - 1, keyCode === KEY_LEFT);
|
||||
} else if (
|
||||
keyCode === KEY_RIGHT || keyCode === KEY_TAB
|
||||
) {
|
||||
this.setCurrent(current + 1, keyCode === KEY_RIGHT);
|
||||
}
|
||||
}}>
|
||||
{button}
|
||||
</div>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const Loader = props => {
|
||||
const { value } = props;
|
||||
|
||||
return (
|
||||
<div className="AlertModal__Loader">
|
||||
<Box
|
||||
className="AlertModal__LoaderProgress"
|
||||
style={{ width: clamp01(value) * 100 + '%' }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 watermelon914 (https://github.com/watermelon914)
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { clamp01 } from 'common/math';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Section, Input, Stack } from '../components';
|
||||
import { KEY_DOWN, KEY_UP, KEY_ENTER, KEY_SPACE } from 'common/keycodes';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
let lastScrollTime = 0;
|
||||
|
||||
export const ListInput = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
title,
|
||||
message,
|
||||
buttons,
|
||||
timeout,
|
||||
} = data;
|
||||
|
||||
// Search
|
||||
const [showSearchBar, setShowSearchBar] = useLocalState(
|
||||
context, 'search_bar', false);
|
||||
const [displayedArray, setDisplayedArray] = useLocalState(
|
||||
context, 'displayed_array', buttons);
|
||||
|
||||
// KeyPress
|
||||
const [searchArray, setSearchArray] = useLocalState(
|
||||
context, 'search_array', []);
|
||||
const [searchIndex, setSearchIndex] = useLocalState(
|
||||
context, 'search_index', 0);
|
||||
const [lastCharCode, setLastCharCode] = useLocalState(
|
||||
context, 'last_char_code', null);
|
||||
|
||||
// Selected Button
|
||||
const [selectedButton, setSelectedButton] = useLocalState(
|
||||
context, 'selected_button', buttons[0]);
|
||||
|
||||
const handleKeyDown = e => {
|
||||
e.preventDefault();
|
||||
if (lastScrollTime > performance.now()) {
|
||||
return;
|
||||
}
|
||||
lastScrollTime = performance.now() + 125;
|
||||
|
||||
if (e.keyCode === KEY_UP || e.keyCode === KEY_DOWN) {
|
||||
let direction = 1;
|
||||
if (e.keyCode === KEY_UP) direction = -1;
|
||||
|
||||
let index = 0;
|
||||
for (index; index < buttons.length; index++) {
|
||||
if (buttons[index] === selectedButton) break;
|
||||
}
|
||||
index += direction;
|
||||
if (index < 0) index = buttons.length - 1;
|
||||
else if (index >= buttons.length) index = 0;
|
||||
setSelectedButton(buttons[index]);
|
||||
setLastCharCode(null);
|
||||
document.getElementById(buttons[index]).focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === KEY_SPACE || e.keyCode === KEY_ENTER) {
|
||||
act("choose", { choice: selectedButton });
|
||||
return;
|
||||
}
|
||||
|
||||
const charCode = String.fromCharCode(e.keyCode).toLowerCase();
|
||||
if (!charCode) return;
|
||||
|
||||
let foundValue;
|
||||
if (charCode === lastCharCode && searchArray.length > 0) {
|
||||
const nextIndex = searchIndex + 1;
|
||||
|
||||
if (nextIndex < searchArray.length) {
|
||||
foundValue = searchArray[nextIndex];
|
||||
setSearchIndex(nextIndex);
|
||||
}
|
||||
else {
|
||||
foundValue = searchArray[0];
|
||||
setSearchIndex(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const resultArray = displayedArray.filter(value =>
|
||||
value.substring(0, 1).toLowerCase() === charCode
|
||||
);
|
||||
|
||||
if (resultArray.length > 0) {
|
||||
setSearchArray(resultArray);
|
||||
setSearchIndex(0);
|
||||
foundValue = resultArray[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (foundValue) {
|
||||
setLastCharCode(charCode);
|
||||
setSelectedButton(foundValue);
|
||||
document.getElementById(foundValue).focus();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Window
|
||||
title={title}
|
||||
width={325}
|
||||
height={325}>
|
||||
{timeout !== undefined && <Loader value={timeout} />}
|
||||
<Window.Content>
|
||||
<Stack fill vertical>
|
||||
<Stack.Item grow>
|
||||
<Section
|
||||
fill
|
||||
scrollable
|
||||
className="ListInput__Section"
|
||||
title={message}
|
||||
tabIndex={0}
|
||||
onKeyDown={handleKeyDown}
|
||||
buttons={(
|
||||
<Button
|
||||
compact
|
||||
icon="search"
|
||||
color="transparent"
|
||||
selected={showSearchBar}
|
||||
tooltip="Search Bar"
|
||||
tooltipPosition="left"
|
||||
onClick={() => {
|
||||
setShowSearchBar(!showSearchBar);
|
||||
setDisplayedArray(buttons);
|
||||
}}
|
||||
/>
|
||||
)}>
|
||||
{displayedArray.map(button => (
|
||||
<Button
|
||||
key={button}
|
||||
fluid
|
||||
color="transparent"
|
||||
id={button}
|
||||
selected={selectedButton === button}
|
||||
onClick={() => {
|
||||
if (selectedButton === button) {
|
||||
act("choose", { choice: button });
|
||||
}
|
||||
else {
|
||||
setSelectedButton(button);
|
||||
}
|
||||
setLastCharCode(null);
|
||||
}}>
|
||||
{button}
|
||||
</Button>
|
||||
))}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
{showSearchBar && (
|
||||
<Stack.Item>
|
||||
<Input
|
||||
fluid
|
||||
onInput={(e, value) => setDisplayedArray(
|
||||
buttons.filter(val => (
|
||||
val.toLowerCase().search(value.toLowerCase()) !== -1
|
||||
))
|
||||
)}
|
||||
/>
|
||||
</Stack.Item>
|
||||
)}
|
||||
<Stack.Item>
|
||||
<Stack textAlign="center">
|
||||
<Stack.Item grow basis={0}>
|
||||
<Button
|
||||
fluid
|
||||
color="good"
|
||||
lineHeight={2}
|
||||
content="Confirm"
|
||||
disabled={selectedButton === null}
|
||||
onClick={() => act("choose", { choice: selectedButton })}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow basis={0}>
|
||||
<Button
|
||||
fluid
|
||||
color="bad"
|
||||
lineHeight={2}
|
||||
content="Cancel"
|
||||
onClick={() => act("cancel")}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const Loader = props => {
|
||||
const { value } = props;
|
||||
return (
|
||||
<div className="ListInput__Loader">
|
||||
<Box
|
||||
className="ListInput__LoaderProgress"
|
||||
style={{
|
||||
width: clamp01(value) * 100 + '%',
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -3788,6 +3788,8 @@
|
||||
#include "code\modules\tgui\modal.dm"
|
||||
#include "code\modules\tgui\states.dm"
|
||||
#include "code\modules\tgui\tgui.dm"
|
||||
#include "code\modules\tgui\tgui_alert.dm"
|
||||
#include "code\modules\tgui\tgui_input_list.dm"
|
||||
#include "code\modules\tgui\tgui_window.dm"
|
||||
#include "code\modules\tgui\modules\_base.dm"
|
||||
#include "code\modules\tgui\modules\admin_shuttle_controller.dm"
|
||||
|
||||
Reference in New Issue
Block a user