prettify, sorry for the big ass commit

This commit is contained in:
Olivier Gagnon
2021-09-04 19:09:30 -04:00
parent 3d7cdb4ef9
commit a18bdd6afc
554 changed files with 91615 additions and 66138 deletions
+62 -61
View File
@@ -4,82 +4,83 @@
import * as React from "react";
type IProps = {
headerClass?: string; // Override default class
headerContent: React.ReactElement;
panelClass?: string; // Override default class
panelContent: React.ReactElement;
panelInitiallyOpened?: boolean;
style?: string;
}
headerClass?: string; // Override default class
headerContent: React.ReactElement;
panelClass?: string; // Override default class
panelContent: React.ReactElement;
panelInitiallyOpened?: boolean;
style?: string;
};
type IState = {
panelOpened: boolean;
}
panelOpened: boolean;
};
export class Accordion extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
constructor(props: IProps) {
super(props);
this.handleHeaderClick = this.handleHeaderClick.bind(this);
this.handleHeaderClick = this.handleHeaderClick.bind(this);
this.state = {
panelOpened: props.panelInitiallyOpened ? props.panelInitiallyOpened : false,
}
this.state = {
panelOpened: props.panelInitiallyOpened
? props.panelInitiallyOpened
: false,
};
}
handleHeaderClick(): void {
this.setState({
panelOpened: !this.state.panelOpened,
});
}
render(): React.ReactNode {
let className = "accordion-header";
if (typeof this.props.headerClass === "string") {
className = this.props.headerClass;
}
handleHeaderClick(): void {
this.setState({
panelOpened: !this.state.panelOpened,
});
}
if (this.state.panelOpened) className += " active";
render(): React.ReactNode {
let className = "accordion-header";
if (typeof this.props.headerClass === "string") {
className = this.props.headerClass;
}
if(this.state.panelOpened) className += " active"
return (
<>
<button className={className} onClick={this.handleHeaderClick}>
{this.props.headerContent}
</button>
<AccordionPanel
opened={this.state.panelOpened}
panelClass={this.props.panelClass}
panelContent={this.props.panelContent}
/>
</>
)
}
return (
<>
<button className={className} onClick={this.handleHeaderClick}>
{this.props.headerContent}
</button>
<AccordionPanel
opened={this.state.panelOpened}
panelClass={this.props.panelClass}
panelContent={this.props.panelContent}
/>
</>
);
}
}
type IPanelProps = {
opened: boolean;
panelClass?: string; // Override default class
panelContent: React.ReactElement;
}
opened: boolean;
panelClass?: string; // Override default class
panelContent: React.ReactElement;
};
class AccordionPanel extends React.Component<IPanelProps, any> {
shouldComponentUpdate(nextProps: IPanelProps): boolean {
return this.props.opened || nextProps.opened;
shouldComponentUpdate(nextProps: IPanelProps): boolean {
return this.props.opened || nextProps.opened;
}
render(): React.ReactNode {
let className = "accordion-panel";
if (typeof this.props.panelClass === "string") {
className = this.props.panelClass;
}
render(): React.ReactNode {
let className = "accordion-panel"
if (typeof this.props.panelClass === "string") {
className = this.props.panelClass;
}
if (!this.props.opened) return <></>;
if(!this.props.opened) return (<></>);
return (
<div className={className} style={{display: "block"}}>
{this.props.panelContent}
</div>
)
}
return (
<div className={className} style={{ display: "block" }}>
{this.props.panelContent}
</div>
);
}
}
+38 -31
View File
@@ -6,44 +6,51 @@
import * as React from "react";
interface IProps {
addClasses?: string;
disabled?: boolean;
id?: string;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string;
tooltip?: string;
addClasses?: string;
disabled?: boolean;
id?: string;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string;
tooltip?: string;
}
type IInnerHTMLMarkup = {
__html: string;
}
__html: string;
};
export function AccordionButton(props: IProps): React.ReactElement {
const hasTooltip = props.tooltip != null && props.tooltip !== "";
const hasTooltip = props.tooltip != null && props.tooltip !== "";
// TODO Add a disabled class for accordion buttons?
let className = "accordion-button";
if (hasTooltip) {
className += " tooltip";
}
// TODO Add a disabled class for accordion buttons?
let className = "accordion-button";
if (hasTooltip) {
className += " tooltip";
}
if (typeof props.addClasses === "string") {
className += ` ${props.addClasses}`;
}
if (typeof props.addClasses === "string") {
className += ` ${props.addClasses}`;
}
// Tooltip will be set using inner HTML
const tooltipMarkup: IInnerHTMLMarkup = {
__html: props.tooltip ? props.tooltip : "",
}
// Tooltip will be set using inner HTML
const tooltipMarkup: IInnerHTMLMarkup = {
__html: props.tooltip ? props.tooltip : "",
};
return (
<button className={className} id={props.id} onClick={props.onClick} style={props.style}>
{props.text}
{
hasTooltip &&
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup}></span>
}
</button>
)
return (
<button
className={className}
id={props.id}
onClick={props.onClick}
style={props.style}
>
{props.text}
{hasTooltip && (
<span
className={"tooltiptext"}
dangerouslySetInnerHTML={tooltipMarkup}
></span>
)}
</button>
);
}
+6 -2
View File
@@ -1,5 +1,9 @@
import * as React from "react";
export function Augmentation(name: string): JSX.Element {
return <span className={"samefont"} style={{'color': 'white'}}>{name}</span>
}
return (
<span className={"samefont"} style={{ color: "white" }}>
{name}
</span>
);
}
+36 -22
View File
@@ -12,31 +12,45 @@ import { Augmentation } from "../../Augmentation/Augmentation";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
type IProps = {
aug: Augmentation;
level?: number | string | null;
}
aug: Augmentation;
level?: number | string | null;
};
export function AugmentationAccordion(props: IProps): React.ReactElement {
let displayName = props.aug.name;
if (props.level != null) {
if (props.aug.name === AugmentationNames.NeuroFluxGovernor) {
displayName += (` - Level ${props.level}`)
}
}
if(typeof props.aug.info === 'string') {
return (
<Accordion
headerContent={<>{displayName}</>}
panelContent={<p><span dangerouslySetInnerHTML={{__html: props.aug.info}} /><br /><br />{props.aug.stats}</p>}
/>
)
let displayName = props.aug.name;
if (props.level != null) {
if (props.aug.name === AugmentationNames.NeuroFluxGovernor) {
displayName += ` - Level ${props.level}`;
}
}
if (typeof props.aug.info === "string") {
return (
<Accordion
headerContent={<>{displayName}</>}
panelContent={<p>{props.aug.info}<br /><br />{props.aug.stats}</p>}
/>
)
<Accordion
headerContent={<>{displayName}</>}
panelContent={
<p>
<span dangerouslySetInnerHTML={{ __html: props.aug.info }} />
<br />
<br />
{props.aug.stats}
</p>
}
/>
);
}
return (
<Accordion
headerContent={<>{displayName}</>}
panelContent={
<p>
{props.aug.info}
<br />
<br />
{props.aug.stats}
</p>
}
/>
);
}
+46 -47
View File
@@ -6,65 +6,64 @@
import * as React from "react";
interface IProps {
intervalTime?: number;
style?: any;
getContent: () => JSX.Element;
getTooltip?: () => JSX.Element;
intervalTime?: number;
style?: any;
getContent: () => JSX.Element;
getTooltip?: () => JSX.Element;
}
interface IState {
i: number;
i: number;
}
export class AutoupdatingParagraph extends React.Component<IProps, IState> {
/**
* Timer ID for auto-updating implementation (returned value from setInterval())
*/
interval = 0;
/**
* Timer ID for auto-updating implementation (returned value from setInterval())
*/
interval = 0;
constructor(props: IProps) {
super(props);
this.state = {
i: 0,
}
}
constructor(props: IProps) {
super(props);
this.state = {
i: 0,
};
}
componentDidMount(): void {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = window.setInterval(() => this.tick(), time);
}
componentDidMount(): void {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = window.setInterval(() => this.tick(), time);
}
componentWillUnmount(): void {
clearInterval(this.interval);
}
componentWillUnmount(): void {
clearInterval(this.interval);
}
tick(): void {
this.setState(prevState => ({
i: prevState.i + 1,
}));
}
tick(): void {
this.setState((prevState) => ({
i: prevState.i + 1,
}));
}
hasTooltip(): boolean {
if (this.props.getTooltip != null) {
return !!this.props.getTooltip()
}
return true;
hasTooltip(): boolean {
if (this.props.getTooltip != null) {
return !!this.props.getTooltip();
}
return true;
}
tooltip(): JSX.Element {
if(!this.props.getTooltip) return <></>;
return this.props.getTooltip();
}
tooltip(): JSX.Element {
if (!this.props.getTooltip) return <></>;
return this.props.getTooltip();
}
render(): React.ReactNode {
return (
<div className="tooltip" style={this.props.style}>
<p>{this.props.getContent()}</p>
{
this.hasTooltip() &&
<span className={"tooltiptext"}>{this.tooltip()}</span>
}
</div>
)
}
render(): React.ReactNode {
return (
<div className="tooltip" style={this.props.style}>
<p>{this.props.getContent()}</p>
{this.hasTooltip() && (
<span className={"tooltiptext"}>{this.tooltip()}</span>
)}
</div>
);
}
}
+61 -55
View File
@@ -7,71 +7,77 @@
import * as React from "react";
interface IProps {
disabled?: boolean;
intervalTime?: number;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string | JSX.Element;
tooltip?: string;
disabled?: boolean;
intervalTime?: number;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string | JSX.Element;
tooltip?: string;
}
interface IState {
i: number;
i: number;
}
type IInnerHTMLMarkup = {
__html: string;
}
__html: string;
};
export class AutoupdatingStdButton extends React.Component<IProps, IState> {
/**
* Timer ID for auto-updating implementation (returned value from setInterval())
*/
interval = 0;
/**
* Timer ID for auto-updating implementation (returned value from setInterval())
*/
interval = 0;
constructor(props: IProps) {
super(props);
this.state = {
i: 0,
}
constructor(props: IProps) {
super(props);
this.state = {
i: 0,
};
}
componentDidMount(): void {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = window.setInterval(() => this.tick(), time);
}
componentWillUnmount(): void {
clearInterval(this.interval);
}
tick(): void {
this.setState((prevState) => ({
i: prevState.i + 1,
}));
}
render(): React.ReactNode {
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
let className = this.props.disabled ? "std-button-disabled" : "std-button";
if (hasTooltip) {
className += " tooltip";
}
componentDidMount(): void {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = window.setInterval(() => this.tick(), time);
}
// Tooltip will eb set using inner HTML
const tooltipMarkup: IInnerHTMLMarkup = {
__html: this.props.tooltip ? this.props.tooltip : "",
};
componentWillUnmount(): void {
clearInterval(this.interval);
}
tick(): void {
this.setState(prevState => ({
i: prevState.i + 1,
}));
}
render(): React.ReactNode {
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
let className = this.props.disabled ? "std-button-disabled" : "std-button";
if (hasTooltip) {
className += " tooltip"
}
// Tooltip will eb set using inner HTML
const tooltipMarkup: IInnerHTMLMarkup = {
__html: this.props.tooltip ? this.props.tooltip : "",
}
return (
<button className={className} onClick={this.props.onClick} style={this.props.style}>
{this.props.text}
{
hasTooltip &&
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup}></span>
}
</button>
)
}
return (
<button
className={className}
onClick={this.props.onClick}
style={this.props.style}
>
{this.props.text}
{hasTooltip && (
<span
className={"tooltiptext"}
dangerouslySetInnerHTML={tooltipMarkup}
></span>
)}
</button>
);
}
}
+122 -60
View File
@@ -1,76 +1,138 @@
// Root React Component for the Corporation UI
import React from "react";
import { Player } from "../../Player";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Reputation } from "./Reputation";
import { Reputation } from "./Reputation";
const Component = React.Component;
export class CharacterOverviewComponent extends Component {
render() {
const intelligence = (
<tr id="character-int-wrapper">
<td className="character-int-cell">Int:&nbsp;</td><td id="character-int-text" className="character-int-cell character-stat-cell">{numeralWrapper.formatSkill(Player.intelligence)}</td>
</tr>
);
render() {
const intelligence = (
<tr id="character-int-wrapper">
<td className="character-int-cell">Int:&nbsp;</td>
<td
id="character-int-text"
className="character-int-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.intelligence)}
</td>
</tr>
);
/*const work = (
/*const work = (
<div>
<p>Work progress:</p>
<p>+{Reputation(Player.workRepGained)} rep</p>
<button onClick={() => Player.startFocusing()} id="character-overview-options-button" className="character-overview-btn">Focus</button>
</div>
);*/
const work = (
<>
<tr className="character-divider"><td colSpan="2">Work progress:</td></tr>
<tr><td colSpan="2">+{Reputation(Player.workRepGained)} rep</td></tr>
<tr><td colSpan="2">
<button onClick={() => Player.startFocusing()} id="character-overview-options-button" className="character-overview-btn">Focus</button>
</td></tr>
</>
);
const work = (
<>
<tr className="character-divider">
<td colSpan="2">Work progress:</td>
</tr>
<tr>
<td colSpan="2">+{Reputation(Player.workRepGained)} rep</td>
</tr>
<tr>
<td colSpan="2">
<button
onClick={() => Player.startFocusing()}
id="character-overview-options-button"
className="character-overview-btn"
>
Focus
</button>
</td>
</tr>
</>
);
return (
<>
<table>
<tbody>
<tr id="character-hp-wrapper">
<td className="character-hp-cell">HP:</td><td id="character-hp-text" className="character-hp-cell character-stat-cell">{numeralWrapper.formatHp(Player.hp) + " / " + numeralWrapper.formatHp(Player.max_hp)}</td>
</tr>
<tr id="character-money-wrapper">
<td className="character-money-cell">Money:&nbsp;</td><td id="character-money-text" className="character-money-cell character-stat-cell">{numeralWrapper.formatMoney(Player.money.toNumber())}</td>
</tr>
<tr id="character-hack-wrapper">
<td className="character-hack-cell">Hack:&nbsp;</td><td id="character-hack-text" className="character-hack-cell character-stat-cell">{numeralWrapper.formatSkill(Player.hacking_skill)}</td>
</tr>
<tr id="character-str-wrapper" className="character-divider">
<td className="character-combat-cell">Str:&nbsp;</td><td id="character-str-text" className="character-combat-cell character-stat-cell">{numeralWrapper.formatSkill(Player.strength)}</td>
</tr>
<tr id="character-def-wrapper">
<td className="character-combat-cell">Def:&nbsp;</td><td id="character-def-text" className="character-combat-cell character-stat-cell">{numeralWrapper.formatSkill(Player.defense)}</td>
</tr>
<tr id="character-dex-wrapper">
<td className="character-combat-cell">Dex:&nbsp;</td><td id="character-dex-text" className="character-combat-cell character-stat-cell">{numeralWrapper.formatSkill(Player.dexterity)}</td>
</tr>
<tr id="character-agi-wrapper">
<td className="character-combat-cell">Agi:&nbsp;</td><td id="character-agi-text" className="character-combat-cell character-stat-cell">{numeralWrapper.formatSkill(Player.agility)}</td>
</tr>
<tr id="character-cha-wrapper" className="character-divider">
<td className="character-cha-cell">Cha:&nbsp;</td><td id="character-cha-text" className="character-cha-cell character-stat-cell">{numeralWrapper.formatSkill(Player.charisma)}</td>
</tr>
{
Player.intelligence >= 1 &&
intelligence
}
{
(Player.isWorking && !Player.focus) &&
work
}
</tbody>
</table>
</>
)
}
}
return (
<>
<table>
<tbody>
<tr id="character-hp-wrapper">
<td className="character-hp-cell">HP:</td>
<td
id="character-hp-text"
className="character-hp-cell character-stat-cell"
>
{numeralWrapper.formatHp(Player.hp) +
" / " +
numeralWrapper.formatHp(Player.max_hp)}
</td>
</tr>
<tr id="character-money-wrapper">
<td className="character-money-cell">Money:&nbsp;</td>
<td
id="character-money-text"
className="character-money-cell character-stat-cell"
>
{numeralWrapper.formatMoney(Player.money.toNumber())}
</td>
</tr>
<tr id="character-hack-wrapper">
<td className="character-hack-cell">Hack:&nbsp;</td>
<td
id="character-hack-text"
className="character-hack-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.hacking_skill)}
</td>
</tr>
<tr id="character-str-wrapper" className="character-divider">
<td className="character-combat-cell">Str:&nbsp;</td>
<td
id="character-str-text"
className="character-combat-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.strength)}
</td>
</tr>
<tr id="character-def-wrapper">
<td className="character-combat-cell">Def:&nbsp;</td>
<td
id="character-def-text"
className="character-combat-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.defense)}
</td>
</tr>
<tr id="character-dex-wrapper">
<td className="character-combat-cell">Dex:&nbsp;</td>
<td
id="character-dex-text"
className="character-combat-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.dexterity)}
</td>
</tr>
<tr id="character-agi-wrapper">
<td className="character-combat-cell">Agi:&nbsp;</td>
<td
id="character-agi-text"
className="character-combat-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.agility)}
</td>
</tr>
<tr id="character-cha-wrapper" className="character-divider">
<td className="character-cha-cell">Cha:&nbsp;</td>
<td
id="character-cha-text"
className="character-cha-cell character-stat-cell"
>
{numeralWrapper.formatSkill(Player.charisma)}
</td>
</tr>
{Player.intelligence >= 1 && intelligence}
{Player.isWorking && !Player.focus && work}
</tbody>
</table>
</>
);
}
}
+76 -56
View File
@@ -1,67 +1,87 @@
import React, { useState } from 'react';
import React, { useState } from "react";
import { KEY } from "../../../utils/helpers/keyCodes";
import { CodingContract, CodingContractType, CodingContractTypes } from "../../CodingContracts";
import {
CodingContract,
CodingContractType,
CodingContractTypes,
} from "../../CodingContracts";
import { ClickableTag, CopyableText } from "./CopyableText";
import { PopupCloseButton } from "./PopupCloseButton";
type IProps = {
c: CodingContract;
popupId: string;
onClose: () => void;
onAttempt: (answer: string) => void;
}
c: CodingContract;
popupId: string;
onClose: () => void;
onAttempt: (answer: string) => void;
};
export function CodingContractPopup(props: IProps): React.ReactElement {
const [answer, setAnswer] = useState("");
const [answer, setAnswer] = useState("");
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setAnswer(event.target.value);
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setAnswer(event.target.value);
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
// React just won't cooperate on this one.
// "React.KeyboardEvent<HTMLInputElement>" seems like the right type but
// whatever ...
const value = (event.target as any).value;
if (event.keyCode === KEY.ENTER && value !== "") {
event.preventDefault();
props.onAttempt(answer);
} else if (event.keyCode === KEY.ESC) {
event.preventDefault();
props.onClose();
}
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
// React just won't cooperate on this one.
// "React.KeyboardEvent<HTMLInputElement>" seems like the right type but
// whatever ...
const value = (event.target as any).value;
if (event.keyCode === KEY.ENTER && value !== "") {
event.preventDefault();
props.onAttempt(answer);
} else if (event.keyCode === KEY.ESC) {
event.preventDefault();
props.onClose();
}
}
const contractType: CodingContractType = CodingContractTypes[props.c.type];
const description = [];
for (const [i, value] of contractType.desc(props.c.data).split('\n').entries())
description.push(<span key={i} dangerouslySetInnerHTML={{__html: value+'<br />'}}></span>);
return (
<div>
<CopyableText value={props.c.type} tag={ClickableTag.Tag_h1} />
<br/><br/>
<p>You are attempting to solve a Coding Contract. You have {props.c.getMaxNumTries() - props.c.tries} tries remaining, after which the contract will self-destruct.</p>
<br/>
<p>{description}</p>
<br/>
<input
className="text-input"
style={{ width:"50%",marginTop:"8px" }}
autoFocus={true}
placeholder="Enter Solution here"
value={answer}
onChange={onChange}
onKeyDown={onKeyDown} />
<PopupCloseButton
popup={props.popupId}
onClose={() => props.onAttempt(answer)}
text={"Solve"} />
<PopupCloseButton
popup={props.popupId}
onClose={props.onClose}
text={"Close"} />
</div>
)
}
const contractType: CodingContractType = CodingContractTypes[props.c.type];
const description = [];
for (const [i, value] of contractType
.desc(props.c.data)
.split("\n")
.entries())
description.push(
<span
key={i}
dangerouslySetInnerHTML={{ __html: value + "<br />" }}
></span>,
);
return (
<div>
<CopyableText value={props.c.type} tag={ClickableTag.Tag_h1} />
<br />
<br />
<p>
You are attempting to solve a Coding Contract. You have{" "}
{props.c.getMaxNumTries() - props.c.tries} tries remaining, after which
the contract will self-destruct.
</p>
<br />
<p>{description}</p>
<br />
<input
className="text-input"
style={{ width: "50%", marginTop: "8px" }}
autoFocus={true}
placeholder="Enter Solution here"
value={answer}
onChange={onChange}
onKeyDown={onKeyDown}
/>
<PopupCloseButton
popup={props.popupId}
onClose={() => props.onAttempt(answer)}
text={"Solve"}
/>
<PopupCloseButton
popup={props.popupId}
onClose={props.onClose}
text={"Close"}
/>
</div>
);
}
+65 -64
View File
@@ -1,82 +1,83 @@
import * as React from "react";
export enum ClickableTag{
Tag_span,
Tag_h1
export enum ClickableTag {
Tag_span,
Tag_h1,
}
type IProps = {
value: string;
tag: ClickableTag;
}
value: string;
tag: ClickableTag;
};
type IState = {
tooltipVisible: boolean;
}
tooltipVisible: boolean;
};
export class CopyableText extends React.Component<IProps, IState> {
public static defaultProps = {
//Default span to prevent destroying current clickables
tag: ClickableTag.Tag_span,
public static defaultProps = {
//Default span to prevent destroying current clickables
tag: ClickableTag.Tag_span,
};
constructor(props: IProps) {
super(props);
this.copy = this.copy.bind(this);
this.tooltipClasses = this.tooltipClasses.bind(this);
this.textClasses = this.textClasses.bind(this);
this.state = {
tooltipVisible: false,
};
constructor(props: IProps) {
super(props);
}
this.copy = this.copy.bind(this);
this.tooltipClasses = this.tooltipClasses.bind(this);
this.textClasses = this.textClasses.bind(this);
copy(): void {
const copyText = document.createElement("textarea");
copyText.value = this.props.value;
document.body.appendChild(copyText);
copyText.select();
copyText.setSelectionRange(0, 1e10);
document.execCommand("copy");
document.body.removeChild(copyText);
this.setState({ tooltipVisible: true });
setTimeout(() => this.setState({ tooltipVisible: false }), 1000);
}
this.state = {
tooltipVisible: false,
}
tooltipClasses(): string {
let classes = "copy_tooltip_text";
if (this.state.tooltipVisible) {
classes += " copy_tooltip_text_visible";
}
copy(): void {
const copyText = document.createElement("textarea");
copyText.value = this.props.value;
document.body.appendChild(copyText);
copyText.select();
copyText.setSelectionRange(0, 1e10);
document.execCommand("copy");
document.body.removeChild(copyText);
this.setState({tooltipVisible: true});
setTimeout(() => this.setState({tooltipVisible: false}), 1000);
return classes;
}
textClasses(): string {
let classes = "copy_tooltip noselect text";
if (this.state.tooltipVisible) {
classes += " copy_tooltip_copied";
}
tooltipClasses(): string {
let classes = "copy_tooltip_text";
if(this.state.tooltipVisible) {
classes += " copy_tooltip_text_visible";
}
return classes;
}
return classes;
render(): React.ReactNode {
switch (this.props.tag) {
case ClickableTag.Tag_h1:
return (
<h1 className={this.textClasses()} onClick={this.copy}>
{this.props.value}
<span className={this.tooltipClasses()}>Copied!</span>
</h1>
);
case ClickableTag.Tag_span:
return (
<span className={this.textClasses()} onClick={this.copy}>
<b>{this.props.value}</b>
<span className={this.tooltipClasses()}>Copied!</span>
</span>
);
}
textClasses(): string {
let classes = "copy_tooltip noselect text";
if(this.state.tooltipVisible) {
classes += " copy_tooltip_copied";
}
return classes;
}
render(): React.ReactNode {
switch (this.props.tag) {
case ClickableTag.Tag_h1:
return (
<h1 className={this.textClasses()} onClick={this.copy}>
{this.props.value}
<span className={this.tooltipClasses()}>Copied!</span>
</h1>)
case ClickableTag.Tag_span:
return (
<span className={this.textClasses()} onClick={this.copy}>
<b>{this.props.value}</b>
<span className={this.tooltipClasses()}>Copied!</span>
</span>)
}
}
}
}
}
+36 -36
View File
@@ -1,53 +1,53 @@
import React, { useEffect, useState } from "react";
function replace(str: string, i: number, char: string): string {
return str.substring(0, i) + char + str.substring(i + 1);
return str.substring(0, i) + char + str.substring(i + 1);
}
interface IProps {
content: string;
content: string;
}
function randomize(char: string): string {
const randFrom = (str: string): string => str[Math.floor(Math.random()*str.length)];
const classes = [
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"1234567890",
" _",
"()[]{}<>",
];
const other = `!@#$%^&*()_+|\\';"/.,?\`~`;
const randFrom = (str: string): string =>
str[Math.floor(Math.random() * str.length)];
const classes = [
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"1234567890",
" _",
"()[]{}<>",
];
const other = `!@#$%^&*()_+|\\';"/.,?\`~`;
for(const c of classes) {
if (c.includes(char)) return randFrom(c);
}
for (const c of classes) {
if (c.includes(char)) return randFrom(c);
}
return randFrom(other);
return randFrom(other);
}
export function CorruptableText(props: IProps): JSX.Element {
const [content, setContent] = useState(props.content);
const [content, setContent] = useState(props.content);
useEffect(() => {
let counter = 5;
const id = setInterval(() => {
counter--;
if (counter > 0)
return;
counter = Math.random() * 5;
const index = Math.random() * content.length;
const letter = content.charAt(index);
setContent(replace(content, index, randomize(letter)));
setTimeout(() => {
setContent(content);
}, 50);
}, 100);
useEffect(() => {
let counter = 5;
const id = setInterval(() => {
counter--;
if (counter > 0) return;
counter = Math.random() * 5;
const index = Math.random() * content.length;
const letter = content.charAt(index);
setContent(replace(content, index, randomize(letter)));
setTimeout(() => {
setContent(content);
}, 50);
}, 100);
return () => {
clearInterval(id);
};
}, []);
return () => {
clearInterval(id);
};
}, []);
return <span>{content}</span>
}
return <span>{content}</span>;
}
+76 -82
View File
@@ -7,99 +7,93 @@ import * as React from "react";
import { EventEmitter } from "../../utils/EventEmitter";
type IProps = {
eventEmitterForReset?: EventEmitter;
id?: string;
eventEmitterForReset?: EventEmitter;
id?: string;
};
type IState = {
errorInfo: string;
hasError: boolean;
}
errorInfo: string;
hasError: boolean;
};
type IErrorInfo = {
componentStack: string;
}
componentStack: string;
};
// TODO: Move this out to a css file
const styleMarkup = {
border: "1px solid red",
display: "inline-block",
margin: "4px",
padding: "4px",
}
border: "1px solid red",
display: "inline-block",
margin: "4px",
padding: "4px",
};
export class ErrorBoundary extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
errorInfo: "",
hasError: false,
}
constructor(props: IProps) {
super(props);
this.state = {
errorInfo: "",
hasError: false,
};
}
componentDidCatch(error: Error, info: IErrorInfo): void {
console.error(`Caught error in React ErrorBoundary. Component stack:`);
console.error(info.componentStack);
}
componentDidMount(): void {
const cb = (): void => {
this.setState({
hasError: false,
});
};
if (this.hasEventEmitter()) {
(this.props.eventEmitterForReset as EventEmitter).addSubscriber({
cb: cb,
id: this.props.id as string,
});
}
}
componentWillUnmount(): void {
if (this.hasEventEmitter()) {
(this.props.eventEmitterForReset as EventEmitter).removeSubscriber(
this.props.id as string,
);
}
}
hasEventEmitter(): boolean {
return (
this.props.eventEmitterForReset != null &&
this.props.eventEmitterForReset instanceof EventEmitter &&
this.props.id != null &&
typeof this.props.id === "string"
);
}
render(): React.ReactNode {
if (this.state.hasError) {
return (
<div style={styleMarkup}>
<p>
{`Error rendering UI. This is (probably) a bug. Please report to game developer.`}
</p>
<p>{`In the meantime, try refreshing the game WITHOUT saving.`}</p>
<p>{`Error info: ${this.state.errorInfo}`}</p>
</div>
);
}
componentDidCatch(error: Error, info: IErrorInfo): void {
console.error(`Caught error in React ErrorBoundary. Component stack:`);
console.error(info.componentStack);
}
return this.props.children;
}
componentDidMount(): void {
const cb = (): void => {
this.setState({
hasError: false,
});
}
if (this.hasEventEmitter()) {
(this.props.eventEmitterForReset as EventEmitter).addSubscriber({
cb: cb,
id: (this.props.id as string),
});
}
}
componentWillUnmount(): void {
if (this.hasEventEmitter()) {
(this.props.eventEmitterForReset as EventEmitter).removeSubscriber((this.props.id as string));
}
}
hasEventEmitter(): boolean {
return this.props.eventEmitterForReset != null &&
this.props.eventEmitterForReset instanceof EventEmitter &&
this.props.id != null &&
typeof this.props.id === "string";
}
render(): React.ReactNode {
if (this.state.hasError) {
return (
<div style={styleMarkup}>
<p>
{
`Error rendering UI. This is (probably) a bug. Please report to game developer.`
}
</p>
<p>
{
`In the meantime, try refreshing the game WITHOUT saving.`
}
</p>
<p>
{
`Error info: ${this.state.errorInfo}`
}
</p>
</div>
)
}
return this.props.children;
}
static getDerivedStateFromError(error: Error): IState {
return {
errorInfo: error.message,
hasError: true,
};
}
static getDerivedStateFromError(error: Error): IState {
return {
errorInfo: error.message,
hasError: true,
};
}
}
+6 -2
View File
@@ -2,5 +2,9 @@ import * as React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
export function Favor(favor: number | string): JSX.Element {
return <span className={"light-yellow samefont"}>{typeof favor === 'number' ? numeralWrapper.formatFavor(favor) : favor}</span>
}
return (
<span className={"light-yellow samefont"}>
{typeof favor === "number" ? numeralWrapper.formatFavor(favor) : favor}
</span>
);
}
+2 -2
View File
@@ -2,5 +2,5 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { Hashes } from "../../ui/React/Hashes";
export function HashRate(hashes: number): JSX.Element {
return Hashes(`${numeralWrapper.formatHashes(hashes)} / sec`);
}
return Hashes(`${numeralWrapper.formatHashes(hashes)} / sec`);
}
+8 -2
View File
@@ -2,5 +2,11 @@ import * as React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
export function Hashes(hashes: number | string): JSX.Element {
return <span className={"money-gold samefont"}>{typeof hashes === 'number' ? numeralWrapper.formatHashes(hashes) : hashes}</span>
}
return (
<span className={"money-gold samefont"}>
{typeof hashes === "number"
? numeralWrapper.formatHashes(hashes)
: hashes}
</span>
);
}
+22 -9
View File
@@ -3,14 +3,27 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps {
money: number | string;
player?: IPlayer;
money: number | string;
player?: IPlayer;
}
export function Money(props: IProps): JSX.Element {
if(props.player !== undefined) {
if(typeof props.money !== 'number') throw new Error('if player if provided, money should be number, contact dev');
if(!props.player.canAfford(props.money))
return <span className={"unbuyable samefont"}>{numeralWrapper.formatMoney(props.money)}</span>
}
return <span className={"money-gold samefont"}>{typeof props.money === 'number' ? numeralWrapper.formatMoney(props.money) : props.money}</span>
}
if (props.player !== undefined) {
if (typeof props.money !== "number")
throw new Error(
"if player if provided, money should be number, contact dev",
);
if (!props.player.canAfford(props.money))
return (
<span className={"unbuyable samefont"}>
{numeralWrapper.formatMoney(props.money)}
</span>
);
}
return (
<span className={"money-gold samefont"}>
{typeof props.money === "number"
? numeralWrapper.formatMoney(props.money)
: props.money}
</span>
);
}
+3 -3
View File
@@ -1,7 +1,7 @@
import React from 'react';
import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
export function MoneyRate(money: number): JSX.Element {
return <Money money={`${numeralWrapper.formatMoney(money)} / sec`} />;
}
return <Money money={`${numeralWrapper.formatMoney(money)} / sec`} />;
}
+32 -31
View File
@@ -1,5 +1,5 @@
/**
* Wrapper around material-ui's Button component that styles it with
* Wrapper around material-ui's Button component that styles it with
* Bitburner's UI theme
*/
@@ -7,37 +7,38 @@ import React from "react";
import { Button, ButtonProps, makeStyles } from "@material-ui/core";
const useStyles = makeStyles({
// Tries to emulate StdButton in buttons.scss
root: {
backgroundColor: "#555",
border: "1px solid #333",
color: "white",
margin: "5px",
padding: "3px 5px",
"&:hover": {
backgroundColor: "#666",
},
},
textPrimary: {
color: "rgb( 144, 202, 249)",
},
textSecondary: {
color: "rgb(244, 143, 177)",
},
disabled: {
backgroundColor: "#333",
color: "#fff",
cursor: "default",
// Tries to emulate StdButton in buttons.scss
root: {
backgroundColor: "#555",
border: "1px solid #333",
color: "white",
margin: "5px",
padding: "3px 5px",
"&:hover": {
backgroundColor: "#666",
},
},
textPrimary: {
color: "rgb( 144, 202, 249)",
},
textSecondary: {
color: "rgb(244, 143, 177)",
},
disabled: {
backgroundColor: "#333",
color: "#fff",
cursor: "default",
},
});
export const MuiButton: React.FC<ButtonProps> = (props: ButtonProps) => {
return (
<Button {...props}
classes={{
...useStyles(),
...props.classes,
}} />
)
}
return (
<Button
{...props}
classes={{
...useStyles(),
...props.classes,
}}
/>
);
};
+19 -18
View File
@@ -1,5 +1,5 @@
/**
* Wrapper around material-ui's Button component that styles it with
* Wrapper around material-ui's Button component that styles it with
* Bitburner's UI theme
*/
@@ -7,23 +7,24 @@ import React from "react";
import { Paper, PaperProps, makeStyles } from "@material-ui/core";
const useStyles = makeStyles({
root: {
backgroundColor: "rgb(30, 30, 30)",
border: "2px solid #000",
borderRadius: "10px",
display: "inline-block",
flexWrap: "wrap",
padding: "10px",
},
root: {
backgroundColor: "rgb(30, 30, 30)",
border: "2px solid #000",
borderRadius: "10px",
display: "inline-block",
flexWrap: "wrap",
padding: "10px",
},
});
export const MuiPaper: React.FC<PaperProps> = (props: PaperProps) => {
return (
<Paper {...props}
classes={{
...useStyles(),
...props.classes,
}} />
)
}
return (
<Paper
{...props}
classes={{
...useStyles(),
...props.classes,
}}
/>
);
};
+60 -56
View File
@@ -1,5 +1,5 @@
/**
* Wrapper around material-ui's TextField component that styles it with
* Wrapper around material-ui's TextField component that styles it with
* Bitburner's UI theme
*/
@@ -7,71 +7,75 @@ import React from "react";
import { makeStyles, TextField, TextFieldProps } from "@material-ui/core";
const backgroundColorStyles = {
backgroundColor: 'rgba(57, 54, 54, 0.9)',
'&:hover': {
backgroundColor: 'rgba(70, 70, 70, 0.9)',
},
backgroundColor: "rgba(57, 54, 54, 0.9)",
"&:hover": {
backgroundColor: "rgba(70, 70, 70, 0.9)",
},
};
const formControlStyles = {
border: '1px solid #e2e2e1',
overflow: 'hidden',
borderRadius: 4,
color: 'white',
...backgroundColorStyles,
border: "1px solid #e2e2e1",
overflow: "hidden",
borderRadius: 4,
color: "white",
...backgroundColorStyles,
};
const useStyles = makeStyles({
root: {
...formControlStyles,
},
root: {
...formControlStyles,
},
});
const useInputStyles = makeStyles({
root: {
...backgroundColorStyles,
color: 'white',
},
focused: {
backgroundColor: 'rgba(70, 70, 70, 0.9)',
},
disabled: {
color: 'white',
},
root: {
...backgroundColorStyles,
color: "white",
},
focused: {
backgroundColor: "rgba(70, 70, 70, 0.9)",
},
disabled: {
color: "white",
},
});
const useLabelStyles = makeStyles({
root: {
color: 'white',
},
focused: {
color: 'white !important', // Need important to override styles from FormLabel
},
disabled: {
color: 'white !important', // Need important to override styles from FormLabel
},
})
root: {
color: "white",
},
focused: {
color: "white !important", // Need important to override styles from FormLabel
},
disabled: {
color: "white !important", // Need important to override styles from FormLabel
},
});
export const MuiTextField: React.FC<TextFieldProps> = (props: TextFieldProps) => {
return (
<TextField {...props}
classes={{
...useStyles(),
...props.classes,
}}
InputProps={{
classes: {
...useInputStyles(),
...props.InputProps?.classes,
},
...props.InputProps,
}}
InputLabelProps={{
classes: {
...useLabelStyles(),
...props.InputLabelProps?.classes,
},
...props.InputLabelProps,
}} />
)
}
export const MuiTextField: React.FC<TextFieldProps> = (
props: TextFieldProps,
) => {
return (
<TextField
{...props}
classes={{
...useStyles(),
...props.classes,
}}
InputProps={{
classes: {
...useInputStyles(),
...props.InputProps?.classes,
},
...props.InputProps,
}}
InputLabelProps={{
classes: {
...useLabelStyles(),
...props.InputLabelProps?.classes,
},
...props.InputLabelProps,
}}
/>
);
};
+16 -15
View File
@@ -1,23 +1,24 @@
/**
* Text (p Element) with Tooltip
*/
import * as React from "react";
import * as React from "react";
export interface IParagraphWithTooltipProps {
style?: any;
content: JSX.Element;
tooltip: string | React.ReactElement | JSX.Element;
style?: any;
content: JSX.Element;
tooltip: string | React.ReactElement | JSX.Element;
}
export class ParagraphWithTooltip extends React.Component<IParagraphWithTooltipProps, any> {
render(): React.ReactNode {
return (
<div className={"tooltip"} style={this.props.style}>
<p>{this.props.content}</p>
<span className={"tooltiptext"}>
{this.props.tooltip}
</span>
</div>
)
}
export class ParagraphWithTooltip extends React.Component<
IParagraphWithTooltipProps,
any
> {
render(): React.ReactNode {
return (
<div className={"tooltip"} style={this.props.style}>
<p>{this.props.content}</p>
<span className={"tooltiptext"}>{this.props.tooltip}</span>
</div>
);
}
}
+18 -18
View File
@@ -6,27 +6,27 @@
import React, { useEffect } from "react";
interface IProps<T> {
content: (props: T) => React.ReactElement;
id: string;
props: T;
removePopup: (id: string) => void;
content: (props: T) => React.ReactElement;
id: string;
props: T;
removePopup: (id: string) => void;
}
export function Popup<T>(props: IProps<T>): React.ReactElement {
function keyDown(event: KeyboardEvent): void {
if(event.key === 'Escape') props.removePopup(props.id);
}
function keyDown(event: KeyboardEvent): void {
if (event.key === "Escape") props.removePopup(props.id);
}
useEffect(() => {
document.addEventListener('keydown', keyDown);
return () => {
document.removeEventListener('keydown', keyDown);
}
});
useEffect(() => {
document.addEventListener("keydown", keyDown);
return () => {
document.removeEventListener("keydown", keyDown);
};
});
return (
<div className={"popup-box-content"} id={`${props.id}-content`}>
{React.createElement(props.content, props.props)}
</div>
)
return (
<div className={"popup-box-content"} id={`${props.id}-content`}>
{React.createElement(props.content, props.props)}
</div>
);
}
+53 -50
View File
@@ -5,68 +5,71 @@
* Should only be used in other React components, otherwise it may not be properly
* unmounted
*/
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { KEY } from "../../../utils/helpers/keyCodes";
import { removeElement } from "../../../utils/uiHelpers/removeElement";
export interface IPopupButtonProps {
class?: string;
popup: HTMLElement | string;
style?: any;
text: string;
onClose?: () => void;
class?: string;
popup: HTMLElement | string;
style?: any;
text: string;
onClose?: () => void;
}
export class PopupButton extends React.Component<IPopupButtonProps, any> {
constructor(props: IPopupButtonProps) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.keyListener = this.keyListener.bind(this);
}
constructor(props: IPopupButtonProps) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.keyListener = this.keyListener.bind(this);
}
componentDidMount(): void {
document.addEventListener("keydown", this.keyListener);
}
componentDidMount(): void {
document.addEventListener("keydown", this.keyListener);
}
componentWillUnmount(): void {
document.removeEventListener("keydown", this.keyListener);
}
componentWillUnmount(): void {
document.removeEventListener("keydown", this.keyListener);
}
handleClick(): void {
if(this.props.onClose)
this.props.onClose();
//We might be able to remove this?
//Clickhandler from the props will override this anyhow.
let popup: HTMLElement | null;
if (typeof this.props.popup === "string") {
popup = document.getElementById(this.props.popup);
} else {
popup = this.props.popup;
}
// TODO Check if this is okay? This is essentially calling to unmount a parent component
if (popup instanceof HTMLElement) {
ReactDOM.unmountComponentAtNode(popup); // Removes everything inside the wrapper container
removeElement(popup); // Removes the wrapper container
}
handleClick(): void {
if (this.props.onClose) this.props.onClose();
//We might be able to remove this?
//Clickhandler from the props will override this anyhow.
let popup: HTMLElement | null;
if (typeof this.props.popup === "string") {
popup = document.getElementById(this.props.popup);
} else {
popup = this.props.popup;
}
keyListener(e: KeyboardEvent): void {
//This doesn't really make sense, a button doesnt have to listen to escape IMO
//Too affraid to remove it since im not sure what it will break.. But yuck..
if (e.keyCode === KEY.ESC) {
this.handleClick();
}
// TODO Check if this is okay? This is essentially calling to unmount a parent component
if (popup instanceof HTMLElement) {
ReactDOM.unmountComponentAtNode(popup); // Removes everything inside the wrapper container
removeElement(popup); // Removes the wrapper container
}
}
render(): React.ReactNode {
const className = this.props.class ? this.props.class : "std-button";
return (
<button className={className} onClick={this.handleClick} style={this.props.style}>
{this.props.text}
</button>
)
keyListener(e: KeyboardEvent): void {
//This doesn't really make sense, a button doesnt have to listen to escape IMO
//Too affraid to remove it since im not sure what it will break.. But yuck..
if (e.keyCode === KEY.ESC) {
this.handleClick();
}
}
}
render(): React.ReactNode {
const className = this.props.class ? this.props.class : "std-button";
return (
<button
className={className}
onClick={this.handleClick}
style={this.props.style}
>
{this.props.text}
</button>
);
}
}
+38 -38
View File
@@ -5,56 +5,56 @@
* Should only be used in other React components, otherwise it may not be properly
* unmounted
*/
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { removeElement } from "../../../utils/uiHelpers/removeElement";
import { IPopupButtonProps, PopupButton } from "./PopupButton";
export interface IPopupCloseButtonProps extends IPopupButtonProps {
class?: string;
popup: HTMLElement | string;
style?: any;
text: string;
onClose: () => void;
class?: string;
popup: HTMLElement | string;
style?: any;
text: string;
onClose: () => void;
}
export class PopupCloseButton extends PopupButton {
constructor(props: IPopupCloseButtonProps) {
super(props);
constructor(props: IPopupCloseButtonProps) {
super(props);
this.closePopup = this.closePopup.bind(this);
this.closePopup = this.closePopup.bind(this);
}
closePopup(): void {
if (this.props.onClose) this.props.onClose();
let popup: HTMLElement | null;
if (typeof this.props.popup === "string") {
popup = document.getElementById(this.props.popup);
} else {
popup = this.props.popup;
}
closePopup(): void {
if(this.props.onClose)
this.props.onClose();
let popup: HTMLElement | null;
if (typeof this.props.popup === "string") {
popup = document.getElementById(this.props.popup);
} else {
popup = this.props.popup;
}
// TODO Check if this is okay? This is essentially calling to unmount a
// parent component
if (popup instanceof HTMLElement) {
// Removes everything inside the wrapper container
ReactDOM.unmountComponentAtNode(popup);
removeElement(popup); // Removes the wrapper container
}
// TODO Check if this is okay? This is essentially calling to unmount a
// parent component
if (popup instanceof HTMLElement) {
// Removes everything inside the wrapper container
ReactDOM.unmountComponentAtNode(popup);
removeElement(popup); // Removes the wrapper container
}
}
render(): React.ReactNode {
const className = this.props.class ? this.props.class : "std-button";
render(): React.ReactNode {
const className = this.props.class ? this.props.class : "std-button";
return (
<button
className={className}
onClick={this.closePopup}
style={this.props.style}>
{this.props.text}
</button>
)
}
return (
<button
className={className}
onClick={this.closePopup}
style={this.props.style}
>
{this.props.text}
</button>
);
}
}
+8 -2
View File
@@ -2,5 +2,11 @@ import * as React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
export function Reputation(reputation: number | string): JSX.Element {
return <span className={"reputation samefont"}>{typeof reputation === 'number' ? numeralWrapper.formatReputation(reputation) : reputation}</span>
}
return (
<span className={"reputation samefont"}>
{typeof reputation === "number"
? numeralWrapper.formatReputation(reputation)
: reputation}
</span>
);
}
+2 -2
View File
@@ -2,5 +2,5 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { Reputation } from "../../ui/React/Reputation";
export function ReputationRate(reputation: number): JSX.Element {
return Reputation(`${numeralWrapper.formatReputation(reputation)} / sec`);
}
return Reputation(`${numeralWrapper.formatReputation(reputation)} / sec`);
}
+59 -47
View File
@@ -10,56 +10,68 @@ import { HacknetServer } from "../../Hacknet/HacknetServer";
// TODO make this an enum when this gets converted to TypeScript
export const ServerType = {
All: 0,
Foreign: 1, // Hackable, non-owned servers
Owned: 2, // Home Computer, Purchased Servers, and Hacknet Servers
Purchased: 3, // Everything from Owned except home computer
}
All: 0,
Foreign: 1, // Hackable, non-owned servers
Owned: 2, // Home Computer, Purchased Servers, and Hacknet Servers
Purchased: 3, // Everything from Owned except home computer
};
export class ServerDropdown extends React.Component {
/**
* Checks if the server should be shown in the dropdown menu, based on the
* 'serverType' property
*/
isValidServer(s) {
const type = this.props.serverType;
switch (type) {
case ServerType.All:
return true;
case ServerType.Foreign:
return (s.hostname !== "home" && !s.purchasedByPlayer);
case ServerType.Owned:
return s.purchasedByPlayer || (s instanceof HacknetServer) || s.hostname === "home";
case ServerType.Purchased:
return s.purchasedByPlayer || (s instanceof HacknetServer);
default:
console.warn(`Invalid ServerType specified for ServerDropdown component: ${type}`);
return false;
}
}
/**
* Given a Server object, creates a Option element
*/
renderOption(s) {
/**
* Checks if the server should be shown in the dropdown menu, based on the
* 'serverType' property
*/
isValidServer(s) {
const type = this.props.serverType;
switch (type) {
case ServerType.All:
return true;
case ServerType.Foreign:
return s.hostname !== "home" && !s.purchasedByPlayer;
case ServerType.Owned:
return (
<option key={s.hostname} value={s.hostname}>{s.hostname}</option>
)
s.purchasedByPlayer ||
s instanceof HacknetServer ||
s.hostname === "home"
);
case ServerType.Purchased:
return s.purchasedByPlayer || s instanceof HacknetServer;
default:
console.warn(
`Invalid ServerType specified for ServerDropdown component: ${type}`,
);
return false;
}
}
/**
* Given a Server object, creates a Option element
*/
renderOption(s) {
return (
<option key={s.hostname} value={s.hostname}>
{s.hostname}
</option>
);
}
render() {
const servers = [];
for (const serverName in AllServers) {
const server = AllServers[serverName];
if (this.isValidServer(server)) {
servers.push(this.renderOption(server));
}
}
render() {
const servers = [];
for (const serverName in AllServers) {
const server = AllServers[serverName];
if (this.isValidServer(server)) {
servers.push(this.renderOption(server));
}
}
return (
<select className={"dropdown"} onChange={this.props.onChange} style={this.props.style}>
{servers}
</select>
)
}
return (
<select
className={"dropdown"}
onChange={this.props.onChange}
style={this.props.style}
>
{servers}
</select>
);
}
}
+16 -18
View File
@@ -11,25 +11,23 @@ import { Accordion } from "./Accordion";
import { SourceFile } from "../../SourceFile/SourceFile";
type IProps = {
level: number;
sf: SourceFile;
}
level: number;
sf: SourceFile;
};
export function SourceFileAccordion(props: IProps): React.ReactElement {
const maxLevel = props.sf.n === 12 ? "∞" : "3";
const maxLevel = props.sf.n === 12 ? "∞" : "3";
return (
<Accordion
headerContent={
<>
{props.sf.name}
<br />
{`Level ${props.level} / ${maxLevel}`}
</>
}
panelContent={
<p dangerouslySetInnerHTML={{__html: props.sf.info}}></p>
}
/>
)
return (
<Accordion
headerContent={
<>
{props.sf.name}
<br />
{`Level ${props.level} / ${maxLevel}`}
</>
}
panelContent={<p dangerouslySetInnerHTML={{ __html: props.sf.info }}></p>}
/>
);
}
+36 -20
View File
@@ -1,24 +1,40 @@
import * as React from "react";
export function StatsTable(rows: any[][], title?: string): React.ReactElement {
let titleElem = <></>
if (title) {
titleElem = <><h2><u>{title}</u></h2><br /></>;
}
return (<>
{titleElem}
<table>
<tbody>
{rows.map((row: any[]) => {
return <tr key={row[0]}>
{row.map((elem: any, i: number) => {
let style = {};
if (i !== 0) style = {textAlign: 'right', paddingLeft: '.25em'};
return <td key={i} style={style}>{elem}</td>
})}
</tr>
let titleElem = <></>;
if (title) {
titleElem = (
<>
<h2>
<u>{title}</u>
</h2>
<br />
</>
);
}
return (
<>
{titleElem}
<table>
<tbody>
{rows.map((row: any[]) => {
return (
<tr key={row[0]}>
{row.map((elem: any, i: number) => {
let style = {};
if (i !== 0)
style = { textAlign: "right", paddingLeft: ".25em" };
return (
<td key={i} style={style}>
{elem}
</td>
);
})}
</tbody>
</table>
</>);
}
</tr>
);
})}
</tbody>
</table>
</>
);
}
+44 -37
View File
@@ -5,50 +5,57 @@
import * as React from "react";
interface IStdButtonProps {
addClasses?: string;
disabled?: boolean;
id?: string;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string | JSX.Element;
tooltip?: string | JSX.Element;
addClasses?: string;
disabled?: boolean;
id?: string;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string | JSX.Element;
tooltip?: string | JSX.Element;
}
type IInnerHTMLMarkup = {
__html: string;
}
__html: string;
};
export function StdButton(props: IStdButtonProps): React.ReactElement {
const hasTooltip = props.tooltip != null && props.tooltip !== "";
let className = props.disabled ? "std-button-disabled" : "std-button";
if (hasTooltip) {
className += " tooltip";
}
const hasTooltip = props.tooltip != null && props.tooltip !== "";
let className = props.disabled ? "std-button-disabled" : "std-button";
if (hasTooltip) {
className += " tooltip";
}
if (typeof props.addClasses === "string") {
className += ` ${props.addClasses}`;
}
if (typeof props.addClasses === "string") {
className += ` ${props.addClasses}`;
}
// Tooltip will be set using inner HTML
let tooltip;
if (hasTooltip) {
if(typeof props.tooltip === 'string') {
const tooltipMarkup: IInnerHTMLMarkup = {
__html: props.tooltip,
}
tooltip = <span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup}></span>
} else {
tooltip = <span className={"tooltiptext"}>{props.tooltip}</span>
}
// Tooltip will be set using inner HTML
let tooltip;
if (hasTooltip) {
if (typeof props.tooltip === "string") {
const tooltipMarkup: IInnerHTMLMarkup = {
__html: props.tooltip,
};
tooltip = (
<span
className={"tooltiptext"}
dangerouslySetInnerHTML={tooltipMarkup}
></span>
);
} else {
tooltip = <span className={"tooltiptext"}>{props.tooltip}</span>;
}
}
return (
<button className={className} id={props.id} onClick={props.onClick} style={props.style}>
{props.text}
{
hasTooltip &&
tooltip
}
</button>
)
return (
<button
className={className}
id={props.id}
onClick={props.onClick}
style={props.style}
>
{props.text}
{hasTooltip && tooltip}
</button>
);
}
+58 -50
View File
@@ -4,57 +4,65 @@
import * as React from "react";
interface IStdButtonPurchasedProps {
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string;
tooltip?: string;
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
style?: any;
text: string;
tooltip?: string;
}
type IInnerHTMLMarkup = {
__html: string;
}
export class StdButtonPurchased extends React.Component<IStdButtonPurchasedProps, any> {
constructor(props: IStdButtonPurchasedProps) {
super(props);
this.hasTooltip = this.hasTooltip.bind(this);
this.tooltip = this.tooltip.bind(this);
}
hasTooltip(): boolean {
return this.props.tooltip != null && this.props.tooltip !== "";
}
tooltip(): string {
if(!this.props.tooltip) return "";
return this.props.tooltip;
}
render(): React.ReactNode {
let className = "std-button-bought";
if (this.hasTooltip()) {
className += " tooltip";
}
// Tooltip will be set using inner HTML
let tooltipMarkup: IInnerHTMLMarkup = {
__html: "",
}
if (this.hasTooltip()) {
tooltipMarkup = {
__html: this.tooltip(),
}
}
return (
<button className={className} onClick={this.props.onClick} style={this.props.style}>
{this.props.text}
{
this.hasTooltip() &&
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup}></span>
}
</button>
)
}
__html: string;
};
export class StdButtonPurchased extends React.Component<
IStdButtonPurchasedProps,
any
> {
constructor(props: IStdButtonPurchasedProps) {
super(props);
this.hasTooltip = this.hasTooltip.bind(this);
this.tooltip = this.tooltip.bind(this);
}
hasTooltip(): boolean {
return this.props.tooltip != null && this.props.tooltip !== "";
}
tooltip(): string {
if (!this.props.tooltip) return "";
return this.props.tooltip;
}
render(): React.ReactNode {
let className = "std-button-bought";
if (this.hasTooltip()) {
className += " tooltip";
}
// Tooltip will be set using inner HTML
let tooltipMarkup: IInnerHTMLMarkup = {
__html: "",
};
if (this.hasTooltip()) {
tooltipMarkup = {
__html: this.tooltip(),
};
}
return (
<button
className={className}
onClick={this.props.onClick}
style={this.props.style}
>
{this.props.text}
{this.hasTooltip() && (
<span
className={"tooltiptext"}
dangerouslySetInnerHTML={tooltipMarkup}
></span>
)}
</button>
);
}
}
+54 -42
View File
@@ -6,8 +6,8 @@
* @param id The (hopefully) unique identifier for the popup container
* @param rootComponent Root React Component for the content (NOT the popup containers themselves)
*/
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Popup } from "./Popup";
@@ -16,18 +16,18 @@ import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
let gameContainer: HTMLElement;
(function() {
function getGameContainer(): void {
const container = document.getElementById("entire-game-container");
if (container == null) {
throw new Error(`Failed to find game container DOM element`)
}
gameContainer = container;
document.removeEventListener("DOMContentLoaded", getGameContainer);
(function () {
function getGameContainer(): void {
const container = document.getElementById("entire-game-container");
if (container == null) {
throw new Error(`Failed to find game container DOM element`);
}
document.addEventListener("DOMContentLoaded", getGameContainer);
gameContainer = container;
document.removeEventListener("DOMContentLoaded", getGameContainer);
}
document.addEventListener("DOMContentLoaded", getGameContainer);
})();
// This variable is used to avoid setting the semi-transparent background
@@ -35,45 +35,57 @@ let gameContainer: HTMLElement;
let deepestPopupId = "";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function createPopup<T>(id: string, rootComponent: (props: T) => React.ReactElement, props: T): HTMLElement | null {
let container = document.getElementById(id);
if (container == null) {
function onClick(this: HTMLElement, event: MouseEvent): any {
if(!event.srcElement) return;
if(!(event.srcElement instanceof HTMLElement)) return;
const clickedId = (event.srcElement as HTMLElement).id;
if(clickedId !== id) return;
removePopup(id);
}
const backgroundColor = deepestPopupId === "" ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)';
container = createElement("div", {
class: "popup-box-container",
display: "flex",
id: id,
backgroundColor: backgroundColor,
clickListener: onClick,
});
gameContainer.appendChild(container);
export function createPopup<T>(
id: string,
rootComponent: (props: T) => React.ReactElement,
props: T,
): HTMLElement | null {
let container = document.getElementById(id);
if (container == null) {
function onClick(this: HTMLElement, event: MouseEvent): any {
if (!event.srcElement) return;
if (!(event.srcElement instanceof HTMLElement)) return;
const clickedId = (event.srcElement as HTMLElement).id;
if (clickedId !== id) return;
removePopup(id);
}
const backgroundColor =
deepestPopupId === "" ? "rgba(0,0,0,0.5)" : "rgba(0,0,0,0)";
container = createElement("div", {
class: "popup-box-container",
display: "flex",
id: id,
backgroundColor: backgroundColor,
clickListener: onClick,
});
if(deepestPopupId === "") deepestPopupId = id;
ReactDOM.render(<Popup content={rootComponent} id={id} props={props} removePopup={removePopup} />, container);
gameContainer.appendChild(container);
}
return container;
if (deepestPopupId === "") deepestPopupId = id;
ReactDOM.render(
<Popup
content={rootComponent}
id={id}
props={props}
removePopup={removePopup}
/>,
container,
);
return container;
}
/**
* Closes a popup created with the createPopup() function above
*/
export function removePopup(id: string): void {
const content = document.getElementById(`${id}`);
if (content == null) return;
const content = document.getElementById(`${id}`);
if (content == null) return;
ReactDOM.unmountComponentAtNode(content);
ReactDOM.unmountComponentAtNode(content);
removeElementById(id);
removeElementById(`${id}-close`);
if(id === deepestPopupId) deepestPopupId = "";
removeElementById(id);
removeElementById(`${id}-close`);
if (id === deepestPopupId) deepestPopupId = "";
}