mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-23 08:03:48 +01:00
Merge pull request #2880 from PhilipArmstead/prompt-additions
Adding support for text/select options in Prompt command
This commit is contained in:
commit
88df76a868
@ -2152,7 +2152,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
message = argsToString([message]);
|
message = argsToString([message]);
|
||||||
SnackbarEvents.emit(message, variant, duration);
|
SnackbarEvents.emit(message, variant, duration);
|
||||||
},
|
},
|
||||||
prompt: function (txt: any): any {
|
prompt: function (txt: any, options?: { type?: string; options?: string[] }): any {
|
||||||
updateDynamicRam("prompt", getRamCost(Player, "prompt"));
|
updateDynamicRam("prompt", getRamCost(Player, "prompt"));
|
||||||
if (!isString(txt)) {
|
if (!isString(txt)) {
|
||||||
txt = JSON.stringify(txt);
|
txt = JSON.stringify(txt);
|
||||||
@ -2161,6 +2161,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
PromptEvent.emit({
|
PromptEvent.emit({
|
||||||
txt: txt,
|
txt: txt,
|
||||||
|
options,
|
||||||
resolve: resolve,
|
resolve: resolve,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
17
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
17
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -5984,19 +5984,22 @@ export interface NS extends Singularity {
|
|||||||
tFormat(milliseconds: number, milliPrecision?: boolean): string;
|
tFormat(milliseconds: number, milliPrecision?: boolean): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt the player with a Yes/No modal.
|
* Prompt the player with an input modal.
|
||||||
* @remarks
|
* @remarks
|
||||||
* RAM cost: 0 GB
|
* RAM cost: 0 GB
|
||||||
*
|
*
|
||||||
* Prompts the player with a dialog box with two options: “Yes” and “No”.
|
* Prompts the player with a dialog box. If `options.type` is undefined or "boolean",
|
||||||
* This function will return true if the player click “Yes” and false if
|
* the player is shown "Yes" and "No" prompts, which return true and false respectively.
|
||||||
* the player clicks “No”. The script’s execution is halted until the player
|
* Passing a type of "text" will give the player a text field and a value of "select"
|
||||||
* selects one of the options.
|
* will show a drop-down field. Choosing type "select" will require an array or object
|
||||||
|
* to be passed via the `options.choices` property.
|
||||||
|
* The script’s execution is halted until the player selects one of the options.
|
||||||
*
|
*
|
||||||
* @param txt - Text to appear in the prompt dialog box.
|
* @param txt - Text to appear in the prompt dialog box.
|
||||||
* @returns True if the player click “Yes” and false if the player clicks “No”.
|
* @param options - Options to modify the prompt the player is shown.
|
||||||
|
* @returns True if the player click “Yes”; false if the player clicks “No”; or the value entered by the player.
|
||||||
*/
|
*/
|
||||||
prompt(txt: string): Promise<boolean>;
|
prompt(txt: string, options?: { type?: "boolean"|"text"|"select"|undefined; choices?: string[] | { [key: string]: string | number } }): Promise<boolean | string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open up a message box.
|
* Open up a message box.
|
||||||
|
@ -691,7 +691,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex as number].code) {
|
if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex as number].code) {
|
||||||
PromptEvent.emit({
|
PromptEvent.emit({
|
||||||
txt: "Do you want to save changes to " + closingScript.fileName + "?",
|
txt: "Do you want to save changes to " + closingScript.fileName + "?",
|
||||||
resolve: (result: boolean) => {
|
resolve: (result: boolean | string) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Save changes
|
// Save changes
|
||||||
closingScript.code = savedScriptCode;
|
closingScript.code = savedScriptCode;
|
||||||
@ -748,7 +748,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
"Do you want to overwrite the current editor content with the contents of " +
|
"Do you want to overwrite the current editor content with the contents of " +
|
||||||
openScript.fileName +
|
openScript.fileName +
|
||||||
" on the server? This cannot be undone.",
|
" on the server? This cannot be undone.",
|
||||||
resolve: (result: boolean) => {
|
resolve: (result: boolean | string) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Save changes
|
// Save changes
|
||||||
openScript.code = serverScriptCode;
|
openScript.code = serverScriptCode;
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, Dispatch, SetStateAction } from "react";
|
||||||
import { EventEmitter } from "../../utils/EventEmitter";
|
import { EventEmitter } from "../../utils/EventEmitter";
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import { KEY } from '../../utils/helpers/keyCodes';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
|
||||||
export const PromptEvent = new EventEmitter<[Prompt]>();
|
export const PromptEvent = new EventEmitter<[Prompt]>();
|
||||||
|
|
||||||
interface Prompt {
|
interface Prompt {
|
||||||
txt: string;
|
txt: string;
|
||||||
resolve: (result: boolean) => void;
|
options?: { type?: string; choices?: string[] | { [key: string]: string | number } };
|
||||||
|
resolve: (result: boolean | string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PromptManager(): React.ReactElement {
|
export function PromptManager(): React.ReactElement {
|
||||||
@ -21,36 +26,150 @@ export function PromptManager(): React.ReactElement {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const valueState = useState('')
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
if (prompt === null) return;
|
if (prompt === null) return;
|
||||||
prompt.resolve(false);
|
prompt.resolve(false);
|
||||||
|
valueState[1]('')
|
||||||
setPrompt(null);
|
setPrompt(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function yes(): void {
|
let promptRenderer;
|
||||||
if (prompt === null) return;
|
switch (prompt?.options?.type) {
|
||||||
prompt.resolve(true);
|
case 'text': {
|
||||||
setPrompt(null);
|
promptRenderer = promptMenuText;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'select': {
|
||||||
|
promptRenderer = promptMenuSelect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
promptRenderer = promptMenuBoolean;
|
||||||
}
|
}
|
||||||
function no(): void {
|
|
||||||
if (prompt === null) return;
|
|
||||||
prompt.resolve(false);
|
|
||||||
setPrompt(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{prompt != null && (
|
{prompt != null && (
|
||||||
<Modal open={true} onClose={close}>
|
<Modal open={true} onClose={close}>
|
||||||
<pre>
|
|
||||||
<Typography>{prompt.txt}</Typography>
|
<Typography>{prompt.txt}</Typography>
|
||||||
</pre>
|
{promptRenderer(prompt, setPrompt, valueState)}
|
||||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', paddingTop: '10px' }}>
|
|
||||||
<Button style={{ marginRight: 'auto' }} onClick={yes}>Yes</Button>
|
|
||||||
<Button onClick={no}>No</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function promptMenuBoolean(prompt: Prompt | null, setPrompt: Dispatch<SetStateAction<Prompt | null>>): React.ReactElement {
|
||||||
|
const yes = (): void => {
|
||||||
|
if (prompt !== null) {
|
||||||
|
prompt.resolve(true);
|
||||||
|
setPrompt(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const no = (): void => {
|
||||||
|
if (prompt !== null) {
|
||||||
|
prompt.resolve(false);
|
||||||
|
setPrompt(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', paddingTop: '10px' }}>
|
||||||
|
<Button style={{ marginRight: 'auto' }} onClick={yes}>Yes</Button>
|
||||||
|
<Button onClick={no}>No</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptMenuText(prompt: Prompt | null, setPrompt: Dispatch<SetStateAction<Prompt | null>>, valueState: [string, Dispatch<SetStateAction<string>>]): React.ReactElement {
|
||||||
|
const [value, setValue] = valueState
|
||||||
|
|
||||||
|
const submit = (): void => {
|
||||||
|
if (prompt !== null) {
|
||||||
|
prompt.resolve(value);
|
||||||
|
setValue('')
|
||||||
|
setPrompt(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (prompt !== null && event.keyCode === KEY.ENTER) {
|
||||||
|
event.preventDefault();
|
||||||
|
submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', paddingTop: '10px' }}>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
value={value}
|
||||||
|
onInput={onInput}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
style={{ flex: '1 0 auto' }}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
submit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptMenuSelect(prompt: Prompt | null, setPrompt: Dispatch<SetStateAction<Prompt | null>>, valueState: [string, Dispatch<SetStateAction<string>>]): React.ReactElement {
|
||||||
|
const [value, setValue] = valueState
|
||||||
|
|
||||||
|
const submit = (): void => {
|
||||||
|
if (prompt !== null) {
|
||||||
|
prompt.resolve(value);
|
||||||
|
setValue('');
|
||||||
|
setPrompt(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = (event: SelectChangeEvent<string>): void => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getItems = (choices: string[] | { [key: string]: string | number }) : React.ReactElement[] => {
|
||||||
|
const content = [];
|
||||||
|
for (const i in choices) {
|
||||||
|
// @ts-ignore
|
||||||
|
content.push(<MenuItem value={i}>{choices[i]}</MenuItem>);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', paddingTop: '10px' }}>
|
||||||
|
<Select onChange={onChange} value={value} style={{ flex: '1 0 auto' }}>
|
||||||
|
{getItems(prompt?.options?.choices || [])}
|
||||||
|
</Select>
|
||||||
|
<Button onClick={submit}>Confirm</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user