mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-01 20:13:51 +01:00
100% mui I think
This commit is contained in:
parent
1e641468f7
commit
0ae8b72188
@ -10,7 +10,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
|
|||||||
import Tabs from "@mui/material/Tabs";
|
import Tabs from "@mui/material/Tabs";
|
||||||
import Tab from "@mui/material/Tab";
|
import Tab from "@mui/material/Tab";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Paper from "@mui/material/Paper";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
|
@ -38,7 +38,7 @@ export interface IGang {
|
|||||||
getWantedPenalty(): number;
|
getWantedPenalty(): number;
|
||||||
calculatePower(): number;
|
calculatePower(): number;
|
||||||
killMember(member: GangMember): void;
|
killMember(member: GangMember): void;
|
||||||
ascendMember(member: GangMember, workerScript: WorkerScript): IAscensionResult;
|
ascendMember(member: GangMember, workerScript?: WorkerScript): IAscensionResult;
|
||||||
getDiscount(): number;
|
getDiscount(): number;
|
||||||
getAllTaskNames(): string[];
|
getAllTaskNames(): string[];
|
||||||
getUpgradeCost(upg: GangMemberUpgrade): number;
|
getUpgradeCost(upg: GangMemberUpgrade): number;
|
||||||
|
@ -3,20 +3,23 @@
|
|||||||
* ascension of a gang member.
|
* ascension of a gang member.
|
||||||
*/
|
*/
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
member: GangMember;
|
member: GangMember;
|
||||||
gang: Gang;
|
|
||||||
popupId: string;
|
|
||||||
onAscend: () => void;
|
onAscend: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AscensionPopup(props: IProps): React.ReactElement {
|
export function AscensionModal(props: IProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -26,9 +29,9 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
function confirm(): void {
|
function confirm(): void {
|
||||||
props.onAscend();
|
props.onAscend();
|
||||||
const res = props.gang.ascendMember(props.member);
|
const res = gang.ascendMember(props.member);
|
||||||
dialogBoxCreate(
|
dialogBoxCreate(
|
||||||
<p>
|
<Typography>
|
||||||
You ascended {props.member.name}!<br />
|
You ascended {props.member.name}!<br />
|
||||||
<br />
|
<br />
|
||||||
Your gang lost {numeralWrapper.formatRespect(res.respect)} respect.
|
Your gang lost {numeralWrapper.formatRespect(res.respect)} respect.
|
||||||
@ -48,13 +51,13 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
Charisma: x{numeralWrapper.format(res.cha, "0.000")}
|
Charisma: x{numeralWrapper.format(res.cha, "0.000")}
|
||||||
<br />
|
<br />
|
||||||
</p>,
|
</Typography>,
|
||||||
);
|
);
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// const ascendBenefits = props.member.getAscensionResults();
|
// const ascendBenefits = props.member.getAscensionResults();
|
||||||
@ -62,8 +65,8 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
|||||||
const postAscend = props.member.getAscensionMultsAfterAscend();
|
const postAscend = props.member.getAscensionMultsAfterAscend();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<pre>
|
<Typography>
|
||||||
Are you sure you want to ascend this member? They will lose all of
|
Are you sure you want to ascend this member? They will lose all of
|
||||||
<br />
|
<br />
|
||||||
their non-Augmentation upgrades and their stats will reset back to 1.
|
their non-Augmentation upgrades and their stats will reset back to 1.
|
||||||
@ -92,13 +95,8 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
|||||||
Charisma: x{numeralWrapper.format(preAscend.cha, "0.000")} => x
|
Charisma: x{numeralWrapper.format(preAscend.cha, "0.000")} => x
|
||||||
{numeralWrapper.format(postAscend.cha, "0.000")}
|
{numeralWrapper.format(postAscend.cha, "0.000")}
|
||||||
<br />
|
<br />
|
||||||
</pre>
|
</Typography>
|
||||||
<button className="std-button" onClick={confirm}>
|
<Button onClick={confirm}>Ascend</Button>
|
||||||
Ascend
|
</Modal>
|
||||||
</button>
|
|
||||||
<button className="std-button" onClick={cancel}>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -5,6 +5,9 @@ import * as React from "react";
|
|||||||
import { Gang } from "../Gang";
|
import { Gang } from "../Gang";
|
||||||
import { CONSTANTS } from "../../Constants";
|
import { CONSTANTS } from "../../Constants";
|
||||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
gang: Gang;
|
gang: Gang;
|
||||||
@ -15,15 +18,17 @@ export function BonusTime(props: IProps): React.ReactElement {
|
|||||||
if ((props.gang.storedCycles / CyclerPerSecond) * 1000 <= 5000) return <></>;
|
if ((props.gang.storedCycles / CyclerPerSecond) * 1000 <= 5000) return <></>;
|
||||||
const bonusMillis = (props.gang.storedCycles / CyclerPerSecond) * 1000;
|
const bonusMillis = (props.gang.storedCycles / CyclerPerSecond) * 1000;
|
||||||
return (
|
return (
|
||||||
<>
|
<Box display="flex">
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Tooltip
|
||||||
Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)}
|
title={
|
||||||
<span className="tooltiptext noselect">
|
<Typography>
|
||||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the
|
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the
|
||||||
browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed.
|
browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed.
|
||||||
</span>
|
</Typography>
|
||||||
</p>
|
}
|
||||||
<br />
|
>
|
||||||
</>
|
<Typography>Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)}</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
10
src/Gang/ui/Context.ts
Normal file
10
src/Gang/ui/Context.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React, { useContext } from "react";
|
||||||
|
import { IGang } from "../IGang";
|
||||||
|
|
||||||
|
export const Context: {
|
||||||
|
Gang: React.Context<IGang>;
|
||||||
|
} = {
|
||||||
|
Gang: React.createContext<IGang>({} as IGang),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGang = () => useContext(Context.Gang);
|
220
src/Gang/ui/EquipmentsSubpage.tsx
Normal file
220
src/Gang/ui/EquipmentsSubpage.tsx
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/**
|
||||||
|
* React Component for the popup that manages gang members upgrades
|
||||||
|
*/
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { GangMemberUpgrades } from "../GangMemberUpgrades";
|
||||||
|
import { GangMemberUpgrade } from "../GangMemberUpgrade";
|
||||||
|
import { Money } from "../../ui/React/Money";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
import { GangMember } from "../GangMember";
|
||||||
|
import { UpgradeType } from "../data/upgrades";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
|
||||||
|
interface INextRevealProps {
|
||||||
|
upgrades: string[];
|
||||||
|
type: UpgradeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function NextReveal(props: INextRevealProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
|
const player = use.Player();
|
||||||
|
const upgrades = Object.keys(GangMemberUpgrades)
|
||||||
|
.filter((upgName: string) => {
|
||||||
|
const upg = GangMemberUpgrades[upgName];
|
||||||
|
if (player.money.gt(gang.getUpgradeCost(upg))) return false;
|
||||||
|
if (upg.type !== props.type) return false;
|
||||||
|
if (props.upgrades.includes(upgName)) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||||
|
|
||||||
|
if (upgrades.length === 0) return <></>;
|
||||||
|
return (
|
||||||
|
<Typography>
|
||||||
|
Next at <Money money={upgrades[0].cost} />
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PurchasedUpgrade({ upgName }: { upgName: string }): React.ReactElement {
|
||||||
|
const upg = GangMemberUpgrades[upgName];
|
||||||
|
return (
|
||||||
|
<Paper sx={{ mx: 1, p: 1 }}>
|
||||||
|
<Box display="flex">
|
||||||
|
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: upg.desc }} />}>
|
||||||
|
<Typography>{upg.name}</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUpgradeButtonProps {
|
||||||
|
upg: GangMemberUpgrade;
|
||||||
|
rerender: () => void;
|
||||||
|
member: GangMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UpgradeButton(props: IUpgradeButtonProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
|
const player = use.Player();
|
||||||
|
function onClick(): void {
|
||||||
|
props.member.buyUpgrade(props.upg, player, gang);
|
||||||
|
props.rerender();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: props.upg.desc }} />}>
|
||||||
|
<span>
|
||||||
|
<Typography>{props.upg.name}</Typography>
|
||||||
|
<Button onClick={onClick}>
|
||||||
|
<Money money={gang.getUpgradeCost(props.upg)} />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPanelProps {
|
||||||
|
member: GangMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
|
const player = use.Player();
|
||||||
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
|
function filterUpgrades(list: string[], type: UpgradeType): GangMemberUpgrade[] {
|
||||||
|
return Object.keys(GangMemberUpgrades)
|
||||||
|
.filter((upgName: string) => {
|
||||||
|
const upg = GangMemberUpgrades[upgName];
|
||||||
|
if (player.money.lt(gang.getUpgradeCost(upg))) return false;
|
||||||
|
if (upg.type !== type) return false;
|
||||||
|
if (list.includes(upgName)) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||||
|
}
|
||||||
|
const weaponUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Weapon);
|
||||||
|
const armorUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Armor);
|
||||||
|
const vehicleUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Vehicle);
|
||||||
|
const rootkitUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Rootkit);
|
||||||
|
const augUpgrades = filterUpgrades(props.member.augmentations, UpgradeType.Augmentation);
|
||||||
|
|
||||||
|
const asc = {
|
||||||
|
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||||
|
str: props.member.calculateAscensionMult(props.member.str_asc_points),
|
||||||
|
def: props.member.calculateAscensionMult(props.member.def_asc_points),
|
||||||
|
dex: props.member.calculateAscensionMult(props.member.dex_asc_points),
|
||||||
|
agi: props.member.calculateAscensionMult(props.member.agi_asc_points),
|
||||||
|
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<Typography variant="h5" color="primary">
|
||||||
|
{props.member.name} ({props.member.task})
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
Hack: {props.member.hack} (x
|
||||||
|
{formatNumber(props.member.hack_mult * asc.hack, 2)})<br />
|
||||||
|
Str: {props.member.str} (x
|
||||||
|
{formatNumber(props.member.str_mult * asc.str, 2)})<br />
|
||||||
|
Def: {props.member.def} (x
|
||||||
|
{formatNumber(props.member.def_mult * asc.def, 2)})<br />
|
||||||
|
Dex: {props.member.dex} (x
|
||||||
|
{formatNumber(props.member.dex_mult * asc.dex, 2)})<br />
|
||||||
|
Agi: {props.member.agi} (x
|
||||||
|
{formatNumber(props.member.agi_mult * asc.agi, 2)})<br />
|
||||||
|
Cha: {props.member.cha} (x
|
||||||
|
{formatNumber(props.member.cha_mult * asc.cha, 2)})
|
||||||
|
</Typography>
|
||||||
|
<Box display="flex" flexWrap="wrap">
|
||||||
|
<Typography>Purchased Upgrades: </Typography>
|
||||||
|
<br />
|
||||||
|
{props.member.upgrades.map((upg: string) => (
|
||||||
|
<PurchasedUpgrade key={upg} upgName={upg} />
|
||||||
|
))}
|
||||||
|
{props.member.augmentations.map((upg: string) => (
|
||||||
|
<PurchasedUpgrade key={upg} upgName={upg} />
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
<Box display="flex" justifyContent="space-around">
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Weapons
|
||||||
|
</Typography>
|
||||||
|
{weaponUpgrades.map((upg) => (
|
||||||
|
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||||
|
))}
|
||||||
|
<NextReveal type={UpgradeType.Weapon} upgrades={props.member.upgrades} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Armor
|
||||||
|
</Typography>
|
||||||
|
{armorUpgrades.map((upg) => (
|
||||||
|
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||||
|
))}
|
||||||
|
<NextReveal type={UpgradeType.Armor} upgrades={props.member.upgrades} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Vehicles
|
||||||
|
</Typography>
|
||||||
|
{vehicleUpgrades.map((upg) => (
|
||||||
|
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||||
|
))}
|
||||||
|
<NextReveal type={UpgradeType.Vehicle} upgrades={props.member.upgrades} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Rootkits
|
||||||
|
</Typography>
|
||||||
|
{rootkitUpgrades.map((upg) => (
|
||||||
|
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||||
|
))}
|
||||||
|
<NextReveal type={UpgradeType.Rootkit} upgrades={props.member.upgrades} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Augmentations
|
||||||
|
</Typography>
|
||||||
|
{augUpgrades.map((upg) => (
|
||||||
|
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||||
|
))}
|
||||||
|
<NextReveal type={UpgradeType.Augmentation} upgrades={props.member.upgrades} />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EquipmentsSubpage(): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
You get a discount on equipment and upgrades based on your gang's respect and power. More respect and power
|
||||||
|
leads to more discounts.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>Discount: -{numeralWrapper.formatPercentage(1 - 1 / gang.getDiscount())}</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
{gang.members.map((member: GangMember) => (
|
||||||
|
<GangMemberUpgradePanel key={member.name} member={member} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,23 +1,36 @@
|
|||||||
/**
|
/**
|
||||||
* React Component for a gang member on the management subpage.
|
* React Component for a gang member on the management subpage.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
import { BBAccordion } from "../../ui/React/BBAccordion";
|
|
||||||
import { GangMemberAccordionContent } from "./GangMemberAccordionContent";
|
import { GangMemberAccordionContent } from "./GangMemberAccordionContent";
|
||||||
|
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import ListItemButton from "@mui/material/ListItemButton";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import Collapse from "@mui/material/Collapse";
|
||||||
|
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||||
|
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
gang: Gang;
|
|
||||||
member: GangMember;
|
member: GangMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GangMemberAccordion(props: IProps): React.ReactElement {
|
export function GangMemberAccordion(props: IProps): React.ReactElement {
|
||||||
|
const [open, setOpen] = useState(true);
|
||||||
return (
|
return (
|
||||||
<BBAccordion
|
<Box component={Paper}>
|
||||||
panelInitiallyOpened={true}
|
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||||
headerContent={<>{props.member.name}</>}
|
<ListItemText primary={<Typography>{props.member.name}</Typography>} />
|
||||||
panelContent={<GangMemberAccordionContent gang={props.gang} member={props.member} />}
|
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||||
/>
|
</ListItemButton>
|
||||||
|
<Collapse in={open} unmountOnExit>
|
||||||
|
<Box sx={{ mx: 4 }}>
|
||||||
|
<GangMemberAccordionContent member={props.member} />
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,27 +6,26 @@ import React, { useState } from "react";
|
|||||||
import { GangMemberStats } from "./GangMemberStats";
|
import { GangMemberStats } from "./GangMemberStats";
|
||||||
import { TaskSelector } from "./TaskSelector";
|
import { TaskSelector } from "./TaskSelector";
|
||||||
import { TaskDescription } from "./TaskDescription";
|
import { TaskDescription } from "./TaskDescription";
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
gang: Gang;
|
|
||||||
member: GangMember;
|
member: GangMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GangMemberAccordionContent(props: IProps): React.ReactElement {
|
export function GangMemberAccordionContent(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
return (
|
return (
|
||||||
<>
|
<Grid container>
|
||||||
<div className={"gang-member-info-div tooltip"}>
|
<Grid item xs={4}>
|
||||||
<GangMemberStats onAscend={() => setRerender((old) => !old)} gang={props.gang} member={props.member} />
|
<GangMemberStats onAscend={() => setRerender((old) => !old)} member={props.member} />
|
||||||
</div>
|
</Grid>
|
||||||
<div className={"gang-member-info-div"}>
|
<Grid item xs={4}>
|
||||||
<TaskSelector onTaskChange={() => setRerender((old) => !old)} gang={props.gang} member={props.member} />
|
<TaskSelector onTaskChange={() => setRerender((old) => !old)} member={props.member} />
|
||||||
</div>
|
</Grid>
|
||||||
<div className={"gang-member-info-div"}>
|
<Grid item xs={4}>
|
||||||
<TaskDescription member={props.member} />
|
<TaskDescription member={props.member} />
|
||||||
</div>
|
</Grid>
|
||||||
</>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,59 +2,21 @@
|
|||||||
* React Component for the list of gang members on the management subpage.
|
* React Component for the list of gang members on the management subpage.
|
||||||
*/
|
*/
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { GangMemberUpgradePopup } from "./GangMemberUpgradePopup";
|
|
||||||
import { GangMemberAccordion } from "./GangMemberAccordion";
|
import { GangMemberAccordion } from "./GangMemberAccordion";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
import { RecruitButton } from "./RecruitButton";
|
import { RecruitButton } from "./RecruitButton";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
|
||||||
interface IProps {
|
export function GangMemberList(): React.ReactElement {
|
||||||
gang: Gang;
|
const gang = useGang();
|
||||||
player: IPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GangMemberList(props: IProps): React.ReactElement {
|
|
||||||
const [filter, setFilter] = useState("");
|
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
function openUpgradePopup(): void {
|
|
||||||
const popupId = `gang-upgrade-popup`;
|
|
||||||
createPopup(popupId, GangMemberUpgradePopup, {
|
|
||||||
gang: props.gang,
|
|
||||||
player: props.player,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFilterChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
setFilter(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const members = props.gang.members.filter(
|
|
||||||
(member: GangMember) => member.name.indexOf(filter) > -1 || member.task.indexOf(filter) > -1,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RecruitButton onRecruit={() => setRerender((old) => !old)} gang={props.gang} />
|
<RecruitButton onRecruit={() => setRerender((old) => !old)} />
|
||||||
<br />
|
|
||||||
<input
|
|
||||||
className="text-input noselect"
|
|
||||||
placeholder="Filter gang member"
|
|
||||||
style={{ margin: "5px", padding: "5px" }}
|
|
||||||
value={filter}
|
|
||||||
onChange={onFilterChange}
|
|
||||||
/>
|
|
||||||
<a className="a-link-button" style={{ display: "inline-block" }} onClick={openUpgradePopup}>
|
|
||||||
Manage Equipment
|
|
||||||
</a>
|
|
||||||
<ul>
|
<ul>
|
||||||
{members.map((member: GangMember) => (
|
{gang.members.map((member: GangMember) => (
|
||||||
<li key={member.name}>
|
<GangMemberAccordion key={member.name} member={member} />
|
||||||
<GangMemberAccordion gang={props.gang} member={member} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
|
@ -2,48 +2,26 @@
|
|||||||
* React Component for the first part of a gang member details.
|
* React Component for the first part of a gang member details.
|
||||||
* Contains skills and exp.
|
* Contains skills and exp.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
import { AscensionPopup } from "./AscensionPopup";
|
import { AscensionModal } from "./AscensionModal";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import { StaticModal } from "../../ui/React/StaticModal";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import HelpIcon from "@mui/icons-material/Help";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
member: GangMember;
|
member: GangMember;
|
||||||
gang: Gang;
|
|
||||||
onAscend: () => void;
|
onAscend: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GangMemberStats(props: IProps): React.ReactElement {
|
export function GangMemberStats(props: IProps): React.ReactElement {
|
||||||
function ascend(): void {
|
const [helpOpen, setHelpOpen] = useState(false);
|
||||||
const popupId = `gang-management-ascend-member ${props.member.name}`;
|
const [ascendOpen, setAscendOpen] = useState(false);
|
||||||
createPopup(popupId, AscensionPopup, {
|
|
||||||
member: props.member,
|
|
||||||
gang: props.gang,
|
|
||||||
popupId: popupId,
|
|
||||||
onAscend: props.onAscend,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openAscensionHelp(): void {
|
|
||||||
dialogBoxCreate(
|
|
||||||
<>
|
|
||||||
Ascending a Gang Member resets the member's progress and stats in exchange for a permanent boost to their stat
|
|
||||||
multipliers.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
The additional stat multiplier that the Gang Member gains upon ascension is based on the amount of exp they
|
|
||||||
have.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
Upon ascension, the member will lose all of its non-Augmentation Equipment and your gang will lose respect equal
|
|
||||||
to the total respect earned by the member.
|
|
||||||
</>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const asc = {
|
const asc = {
|
||||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||||
@ -56,26 +34,36 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span className="tooltiptext smallfont">
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x
|
Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x
|
||||||
{numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)} Asc)
|
{numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)}{" "}
|
||||||
|
Asc)
|
||||||
<br />
|
<br />
|
||||||
St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)}
|
St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)}
|
||||||
(x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)} Asc)
|
(x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)}{" "}
|
||||||
|
Asc)
|
||||||
<br />
|
<br />
|
||||||
Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)}
|
Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)}
|
||||||
(x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)} Asc)
|
(x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)}{" "}
|
||||||
|
Asc)
|
||||||
<br />
|
<br />
|
||||||
Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)}
|
Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)}
|
||||||
(x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)} Asc)
|
(x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)}{" "}
|
||||||
|
Asc)
|
||||||
<br />
|
<br />
|
||||||
Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)}
|
Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)}
|
||||||
(x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)} Asc)
|
(x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)}{" "}
|
||||||
|
Asc)
|
||||||
<br />
|
<br />
|
||||||
Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)}
|
Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)}
|
||||||
(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)} Asc)
|
(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)}{" "}
|
||||||
</span>
|
Asc)
|
||||||
<pre>
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
|
Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
|
||||||
<br />
|
<br />
|
||||||
Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
|
Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
|
||||||
@ -88,16 +76,35 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
|
Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
|
||||||
<br />
|
<br />
|
||||||
</pre>
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
<br />
|
<br />
|
||||||
{props.member.canAscend() && (
|
{props.member.canAscend() && (
|
||||||
<>
|
<>
|
||||||
<button className="accordion-button noselect" onClick={ascend}>
|
<Button onClick={() => setAscendOpen(true)}>Ascend</Button>
|
||||||
Ascend
|
<AscensionModal
|
||||||
</button>
|
open={ascendOpen}
|
||||||
<div className="help-tip noselect" style={{ marginTop: "5px" }} onClick={openAscensionHelp}>
|
onClose={() => setAscendOpen(false)}
|
||||||
?
|
member={props.member}
|
||||||
</div>
|
onAscend={props.onAscend}
|
||||||
|
/>
|
||||||
|
<IconButton onClick={() => setHelpOpen(true)}>
|
||||||
|
<HelpIcon />
|
||||||
|
</IconButton>
|
||||||
|
<StaticModal open={helpOpen} onClose={() => setHelpOpen(false)}>
|
||||||
|
<Typography>
|
||||||
|
Ascending a Gang Member resets the member's progress and stats in exchange for a permanent boost to their
|
||||||
|
stat multipliers.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The additional stat multiplier that the Gang Member gains upon ascension is based on the amount of exp
|
||||||
|
they have.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Upon ascension, the member will lose all of its non-Augmentation Equipment and your gang will lose respect
|
||||||
|
equal to the total respect earned by the member.
|
||||||
|
</Typography>
|
||||||
|
</StaticModal>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,224 +0,0 @@
|
|||||||
/**
|
|
||||||
* React Component for the popup that manages gang members upgrades
|
|
||||||
*/
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
|
||||||
import { GangMemberUpgrades } from "../GangMemberUpgrades";
|
|
||||||
import { GangMemberUpgrade } from "../GangMemberUpgrade";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { Money } from "../../ui/React/Money";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { GangMember } from "../GangMember";
|
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { UpgradeType } from "../data/upgrades";
|
|
||||||
|
|
||||||
interface INextRevealProps {
|
|
||||||
gang: Gang;
|
|
||||||
upgrades: string[];
|
|
||||||
type: UpgradeType;
|
|
||||||
player: IPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function NextReveal(props: INextRevealProps): React.ReactElement {
|
|
||||||
const upgrades = Object.keys(GangMemberUpgrades)
|
|
||||||
.filter((upgName: string) => {
|
|
||||||
const upg = GangMemberUpgrades[upgName];
|
|
||||||
if (props.player.money.gt(props.gang.getUpgradeCost(upg))) return false;
|
|
||||||
if (upg.type !== props.type) return false;
|
|
||||||
if (props.upgrades.includes(upgName)) return false;
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
|
||||||
|
|
||||||
if (upgrades.length === 0) return <></>;
|
|
||||||
return (
|
|
||||||
<p>
|
|
||||||
Next at <Money money={upgrades[0].cost} />
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IPanelProps {
|
|
||||||
member: GangMember;
|
|
||||||
gang: Gang;
|
|
||||||
player: IPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
|
||||||
const setRerender = useState(false)[1];
|
|
||||||
function filterUpgrades(list: string[], type: UpgradeType): GangMemberUpgrade[] {
|
|
||||||
return Object.keys(GangMemberUpgrades)
|
|
||||||
.filter((upgName: string) => {
|
|
||||||
const upg = GangMemberUpgrades[upgName];
|
|
||||||
if (props.player.money.lt(props.gang.getUpgradeCost(upg))) return false;
|
|
||||||
if (upg.type !== type) return false;
|
|
||||||
if (list.includes(upgName)) return false;
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
|
||||||
}
|
|
||||||
const weaponUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Weapon);
|
|
||||||
const armorUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Armor);
|
|
||||||
const vehicleUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Vehicle);
|
|
||||||
const rootkitUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Rootkit);
|
|
||||||
const augUpgrades = filterUpgrades(props.member.augmentations, UpgradeType.Augmentation);
|
|
||||||
|
|
||||||
function purchasedUpgrade(upgName: string): React.ReactElement {
|
|
||||||
const upg = GangMemberUpgrades[upgName];
|
|
||||||
return (
|
|
||||||
<div key={upgName} className="gang-owned-upgrade tooltip">
|
|
||||||
{upg.name}
|
|
||||||
<span className="tooltiptext" dangerouslySetInnerHTML={{ __html: upg.desc }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgradeButton(upg: GangMemberUpgrade, left = false): React.ReactElement {
|
|
||||||
function onClick(): void {
|
|
||||||
props.member.buyUpgrade(upg, props.player, props.gang);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
key={upg.name}
|
|
||||||
className="a-link-button tooltip"
|
|
||||||
style={{
|
|
||||||
margin: "2px",
|
|
||||||
padding: "2px",
|
|
||||||
display: "block",
|
|
||||||
fontSize: "11px",
|
|
||||||
}}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{upg.name} - <Money money={props.gang.getUpgradeCost(upg)} player={props.player} />
|
|
||||||
<span className={left ? "tooltiptextleft" : "tooltiptext"} dangerouslySetInnerHTML={{ __html: upg.desc }} />
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const asc = {
|
|
||||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
|
||||||
str: props.member.calculateAscensionMult(props.member.str_asc_points),
|
|
||||||
def: props.member.calculateAscensionMult(props.member.def_asc_points),
|
|
||||||
dex: props.member.calculateAscensionMult(props.member.dex_asc_points),
|
|
||||||
agi: props.member.calculateAscensionMult(props.member.agi_asc_points),
|
|
||||||
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div style={{ border: "1px solid white" }}>
|
|
||||||
<h1>
|
|
||||||
{props.member.name}({props.member.task})
|
|
||||||
</h1>
|
|
||||||
<pre style={{ fontSize: "14px", display: "inline-block", width: "20%" }}>
|
|
||||||
Hack: {props.member.hack} (x
|
|
||||||
{formatNumber(props.member.hack_mult * asc.hack, 2)})<br />
|
|
||||||
Str: {props.member.str} (x
|
|
||||||
{formatNumber(props.member.str_mult * asc.str, 2)})<br />
|
|
||||||
Def: {props.member.def} (x
|
|
||||||
{formatNumber(props.member.def_mult * asc.def, 2)})<br />
|
|
||||||
Dex: {props.member.dex} (x
|
|
||||||
{formatNumber(props.member.dex_mult * asc.dex, 2)})<br />
|
|
||||||
Agi: {props.member.agi} (x
|
|
||||||
{formatNumber(props.member.agi_mult * asc.agi, 2)})<br />
|
|
||||||
Cha: {props.member.cha} (x
|
|
||||||
{formatNumber(props.member.cha_mult * asc.cha, 2)})
|
|
||||||
</pre>
|
|
||||||
<div className="gang-owned-upgrades-div noselect">
|
|
||||||
Purchased Upgrades: {props.member.upgrades.map((upg: string) => purchasedUpgrade(upg))}
|
|
||||||
{props.member.augmentations.map((upg: string) => purchasedUpgrade(upg))}
|
|
||||||
</div>
|
|
||||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
|
||||||
<h2>Weapons</h2>
|
|
||||||
{weaponUpgrades.map((upg) => upgradeButton(upg))}
|
|
||||||
<NextReveal
|
|
||||||
gang={props.gang}
|
|
||||||
type={UpgradeType.Weapon}
|
|
||||||
player={props.player}
|
|
||||||
upgrades={props.member.upgrades}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
|
||||||
<h2>Armor</h2>
|
|
||||||
{armorUpgrades.map((upg) => upgradeButton(upg))}
|
|
||||||
<NextReveal gang={props.gang} type={UpgradeType.Armor} player={props.player} upgrades={props.member.upgrades} />
|
|
||||||
</div>
|
|
||||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
|
||||||
<h2>Vehicles</h2>
|
|
||||||
{vehicleUpgrades.map((upg) => upgradeButton(upg))}
|
|
||||||
<NextReveal
|
|
||||||
gang={props.gang}
|
|
||||||
type={UpgradeType.Vehicle}
|
|
||||||
player={props.player}
|
|
||||||
upgrades={props.member.upgrades}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
|
||||||
<h2>Rootkits</h2>
|
|
||||||
{rootkitUpgrades.map((upg) => upgradeButton(upg, true))}
|
|
||||||
<NextReveal
|
|
||||||
gang={props.gang}
|
|
||||||
type={UpgradeType.Rootkit}
|
|
||||||
player={props.player}
|
|
||||||
upgrades={props.member.upgrades}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
|
||||||
<h2>Augmentations</h2>
|
|
||||||
{augUpgrades.map((upg) => upgradeButton(upg, true))}
|
|
||||||
<NextReveal
|
|
||||||
gang={props.gang}
|
|
||||||
type={UpgradeType.Augmentation}
|
|
||||||
player={props.player}
|
|
||||||
upgrades={props.member.upgrades}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
gang: Gang;
|
|
||||||
player: IPlayer;
|
|
||||||
popupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GangMemberUpgradePopup(props: IProps): React.ReactElement {
|
|
||||||
const setRerender = useState(false)[1];
|
|
||||||
const [filter, setFilter] = useState("");
|
|
||||||
|
|
||||||
function closePopup(this: Window, ev: KeyboardEvent): void {
|
|
||||||
if (ev.keyCode !== 27) return;
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener<"keydown">("keydown", closePopup);
|
|
||||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
|
||||||
return () => {
|
|
||||||
clearInterval(id);
|
|
||||||
window.removeEventListener<"keydown">("keydown", closePopup);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
className="text-input noselect"
|
|
||||||
value={filter}
|
|
||||||
placeholder="Filter gang member"
|
|
||||||
onChange={(event) => setFilter(event.target.value)}
|
|
||||||
/>
|
|
||||||
<p className="tooltip" style={{ marginLeft: "6px", display: "inline-block" }}>
|
|
||||||
Discount: -{numeralWrapper.formatPercentage(1 - 1 / props.gang.getDiscount())}
|
|
||||||
<span className="tooltiptext noselect">
|
|
||||||
You get a discount on equipment and upgrades based on your gang's respect and power. More respect and power
|
|
||||||
leads to more discounts.
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
{props.gang.members.map((member: GangMember) => (
|
|
||||||
<GangMemberUpgradePanel key={member.name} player={props.player} gang={props.gang} member={member} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -4,48 +4,49 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { ManagementSubpage } from "./ManagementSubpage";
|
import { ManagementSubpage } from "./ManagementSubpage";
|
||||||
import { TerritorySubpage } from "./TerritorySubpage";
|
import { TerritorySubpage } from "./TerritorySubpage";
|
||||||
|
import { EquipmentsSubpage } from "./EquipmentsSubpage";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { Factions } from "../../Faction/Factions";
|
import { Factions } from "../../Faction/Factions";
|
||||||
|
import { Context } from "./Context";
|
||||||
|
|
||||||
|
import Tabs from "@mui/material/Tabs";
|
||||||
|
import Tab from "@mui/material/Tab";
|
||||||
|
|
||||||
|
enum Page {
|
||||||
|
Management,
|
||||||
|
Equipment,
|
||||||
|
Territory,
|
||||||
|
}
|
||||||
|
|
||||||
export function GangRoot(): React.ReactElement {
|
export function GangRoot(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const router = use.Router();
|
|
||||||
const gang = (function () {
|
const gang = (function () {
|
||||||
if (player.gang === null) throw new Error("Gang should not be null");
|
if (player.gang === null) throw new Error("Gang should not be null");
|
||||||
return player.gang;
|
return player.gang;
|
||||||
})();
|
})();
|
||||||
const [management, setManagement] = useState(true);
|
const [value, setValue] = React.useState(0);
|
||||||
|
|
||||||
|
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||||
|
setValue(tab);
|
||||||
|
}
|
||||||
|
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
const id = setInterval(() => setRerender((old) => !old), 200);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function back(): void {
|
|
||||||
router.toFaction(Factions[gang.facName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="gang-container">
|
<Context.Gang.Provider value={gang}>
|
||||||
<a className="a-link-button" style={{ display: "inline-block" }} onClick={back}>
|
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||||
Back
|
<Tab label="Management" />
|
||||||
</a>
|
<Tab label="Equipment" />
|
||||||
<a
|
<Tab label="Territory" />
|
||||||
className={management ? "a-link-button-inactive" : "a-link-button"}
|
</Tabs>
|
||||||
style={{ display: "inline-block" }}
|
{value === 0 && <ManagementSubpage />}
|
||||||
onClick={() => setManagement(true)}
|
{value === 1 && <EquipmentsSubpage />}
|
||||||
>
|
{value === 2 && <TerritorySubpage />}
|
||||||
Gang Management
|
</Context.Gang.Provider>
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className={!management ? "a-link-button-inactive" : "a-link-button"}
|
|
||||||
style={{ display: "inline-block" }}
|
|
||||||
onClick={() => setManagement(false)}
|
|
||||||
>
|
|
||||||
Gang Territory
|
|
||||||
</a>
|
|
||||||
{management ? <ManagementSubpage gang={gang} player={player} /> : <TerritorySubpage gang={gang} />}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Factions } from "../../Faction/Factions";
|
import { Factions } from "../../Faction/Factions";
|
||||||
import { Gang } from "../Gang";
|
|
||||||
|
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
@ -12,13 +11,14 @@ import { MoneyRate } from "../../ui/React/MoneyRate";
|
|||||||
import { Reputation } from "../../ui/React/Reputation";
|
import { Reputation } from "../../ui/React/Reputation";
|
||||||
import { AllGangs } from "../AllGangs";
|
import { AllGangs } from "../AllGangs";
|
||||||
import { BonusTime } from "./BonusTime";
|
import { BonusTime } from "./BonusTime";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
interface IProps {
|
export function GangStats(): React.ReactElement {
|
||||||
gang: Gang;
|
const gang = useGang();
|
||||||
}
|
const territoryMult = AllGangs[gang.facName].territory * 100;
|
||||||
|
|
||||||
export function GangStats(props: IProps): React.ReactElement {
|
|
||||||
const territoryMult = AllGangs[props.gang.facName].territory * 100;
|
|
||||||
let territoryStr;
|
let territoryStr;
|
||||||
if (territoryMult <= 0) {
|
if (territoryMult <= 0) {
|
||||||
territoryStr = formatNumber(0, 2);
|
territoryStr = formatNumber(0, 2);
|
||||||
@ -30,46 +30,59 @@ export function GangStats(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Respect: {numeralWrapper.formatRespect(props.gang.respect)} (
|
<Tooltip
|
||||||
{numeralWrapper.formatRespect(5 * props.gang.respectGainRate)} / sec)
|
title={
|
||||||
<span className="tooltiptext">
|
<Typography>
|
||||||
Represents the amount of respect your gang has from other gangs and criminal organizations. Your respect
|
Represents the amount of respect your gang has from other gangs and criminal organizations. Your respect
|
||||||
affects the amount of money your gang members will earn, and also determines how much reputation you are
|
affects the amount of money your gang members will earn, and also determines how much reputation you are
|
||||||
earning with your gang's corresponding Faction.
|
earning with your gang's corresponding Faction.
|
||||||
</span>
|
</Typography>
|
||||||
</p>
|
}
|
||||||
<br />
|
>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
Wanted Level: {numeralWrapper.formatWanted(props.gang.wanted)} (
|
Respect: {numeralWrapper.formatRespect(gang.respect)} (
|
||||||
{numeralWrapper.formatWanted(5 * props.gang.wantedGainRate)} / sec)
|
{numeralWrapper.formatRespect(5 * gang.respectGainRate)} / sec)
|
||||||
<span className="tooltiptext">
|
</Typography>
|
||||||
Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder it
|
</Tooltip>
|
||||||
will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1.
|
</Box>
|
||||||
</span>
|
|
||||||
</p>
|
<Box display="flex">
|
||||||
<br />
|
<Tooltip
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
title={
|
||||||
Wanted Level Penalty: -{formatNumber((1 - props.gang.getWantedPenalty()) * 100, 2)}%
|
<Typography>
|
||||||
<span className="tooltiptext">Penalty for respect and money gain rates due to Wanted Level</span>
|
Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder
|
||||||
</p>
|
it will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1.
|
||||||
<br />
|
</Typography>
|
||||||
<div>
|
}
|
||||||
<p style={{ display: "inline-block" }}>
|
>
|
||||||
Money gain rate: <MoneyRate money={5 * props.gang.moneyGainRate} />
|
<Typography>
|
||||||
</p>
|
Wanted Level: {numeralWrapper.formatWanted(gang.wanted)} (
|
||||||
</div>
|
{numeralWrapper.formatWanted(5 * gang.wantedGainRate)} / sec)
|
||||||
<br />
|
</Typography>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
</Tooltip>
|
||||||
Territory: {territoryStr}%
|
</Box>
|
||||||
<span className="tooltiptext">The percentage of total territory your Gang controls</span>
|
|
||||||
</p>
|
<Box display="flex">
|
||||||
<br />
|
<Tooltip title={<Typography>Penalty for respect and money gain rates due to Wanted Level</Typography>}>
|
||||||
<p style={{ display: "inline-block" }}>
|
<Typography>Wanted Level Penalty: -{formatNumber((1 - gang.getWantedPenalty()) * 100, 2)}%</Typography>
|
||||||
Faction reputation: <Reputation reputation={Factions[props.gang.facName].playerReputation} />
|
</Tooltip>
|
||||||
</p>
|
</Box>
|
||||||
<br />
|
|
||||||
<BonusTime gang={props.gang} />
|
<Typography>
|
||||||
|
Money gain rate: <MoneyRate money={5 * gang.moneyGainRate} />
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Box display="flex">
|
||||||
|
<Tooltip title={<Typography>The percentage of total territory your Gang controls</Typography>}>
|
||||||
|
<Typography>Territory: {territoryStr}%</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<Typography>
|
||||||
|
Faction reputation: <Reputation reputation={Factions[gang.facName].playerReputation} />
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<BonusTime gang={gang} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,16 @@
|
|||||||
* React Component for the subpage that manages gang members, the main page.
|
* React Component for the subpage that manages gang members, the main page.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { GangStats } from "./GangStats";
|
import { GangStats } from "./GangStats";
|
||||||
import { Gang } from "../Gang";
|
|
||||||
import { GangMemberList } from "./GangMemberList";
|
import { GangMemberList } from "./GangMemberList";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
export function ManagementSubpage(): React.ReactElement {
|
||||||
gang: Gang;
|
const gang = useGang();
|
||||||
player: IPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ManagementSubpage(props: IProps): React.ReactElement {
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "block" }}>
|
<>
|
||||||
<p className="noselect" style={{ width: "70%" }}>
|
<Typography>
|
||||||
This page is used to manage your gang members and get an overview of your gang's stats.
|
This page is used to manage your gang members and get an overview of your gang's stats.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@ -23,7 +19,7 @@ export function ManagementSubpage(props: IProps): React.ReactElement {
|
|||||||
too difficult. Consider training that member's stats or choosing an easier task. The tasks closer to the top of
|
too difficult. Consider training that member's stats or choosing an easier task. The tasks closer to the top of
|
||||||
the dropdown list are generally easier. Alternatively, the gang member's low production might be due to the fact
|
the dropdown list are generally easier. Alternatively, the gang member's low production might be due to the fact
|
||||||
that your wanted level is too high. Consider assigning a few members to the '
|
that your wanted level is too high. Consider assigning a few members to the '
|
||||||
{props.gang.isHackingGang ? "Ethical Hacking" : "Vigilante Justice"}' task to lower your wanted level.
|
{gang.isHackingGang ? "Ethical Hacking" : "Vigilante Justice"}' task to lower your wanted level.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Installing Augmentations does NOT reset your progress with your Gang. Furthermore, after installing
|
Installing Augmentations does NOT reset your progress with your Gang. Furthermore, after installing
|
||||||
@ -31,11 +27,11 @@ export function ManagementSubpage(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
You can also manage your gang programmatically through Netscript using the Gang API
|
You can also manage your gang programmatically through Netscript using the Gang API
|
||||||
</p>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<GangStats gang={props.gang} />
|
<GangStats />
|
||||||
<br />
|
<br />
|
||||||
<GangMemberList gang={props.gang} player={props.player} />
|
<GangMemberList />
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,42 @@
|
|||||||
/**
|
/**
|
||||||
* React Component for the recruitment button and text on the gang main page.
|
* React Component for the recruitment button and text on the gang main page.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { Gang } from "../Gang";
|
import { RecruitModal } from "./RecruitModal";
|
||||||
import { RecruitPopup } from "./RecruitPopup";
|
|
||||||
import { GangConstants } from "../data/Constants";
|
import { GangConstants } from "../data/Constants";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
import { useGang } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
gang: Gang;
|
|
||||||
onRecruit: () => void;
|
onRecruit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RecruitButton(props: IProps): React.ReactElement {
|
export function RecruitButton(props: IProps): React.ReactElement {
|
||||||
if (props.gang.members.length >= GangConstants.MaximumGangMembers) {
|
const gang = useGang();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
if (gang.members.length >= GangConstants.MaximumGangMembers) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props.gang.canRecruitMember()) {
|
if (!gang.canRecruitMember()) {
|
||||||
const respect = props.gang.getRespectNeededToRecruitMember();
|
const respect = gang.getRespectNeededToRecruitMember();
|
||||||
return (
|
return (
|
||||||
<>
|
<Box display="flex" alignItems="center">
|
||||||
<a className="a-link-button-inactive" style={{ display: "inline-block", margin: "10px" }}>
|
<Button sx={{ mx: 1 }} disabled>
|
||||||
Recruit Gang Member
|
Recruit Gang Member
|
||||||
</a>
|
</Button>
|
||||||
<p style={{ margin: "10px", color: "red", display: "inline-block" }}>
|
<Typography>{formatNumber(respect, 2)} respect needed to recruit next member</Typography>
|
||||||
{formatNumber(respect, 2)} respect needed to recruit next member
|
</Box>
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(): void {
|
|
||||||
const popupId = "recruit-gang-member-popup";
|
|
||||||
createPopup(popupId, RecruitPopup, {
|
|
||||||
gang: props.gang,
|
|
||||||
popupId: popupId,
|
|
||||||
onRecruit: props.onRecruit,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<a className="a-link-button" onClick={onClick} style={{ display: "inline-block", margin: "10px" }}>
|
<Button onClick={() => setOpen(true)}>Recruit Gang Member</Button>
|
||||||
Recruit Gang Member
|
<RecruitModal open={open} onClose={() => setOpen(false)} onRecruit={props.onRecruit} />
|
||||||
</a>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,38 +2,35 @@
|
|||||||
* React Component for the popup used to recruit new gang members.
|
* React Component for the popup used to recruit new gang members.
|
||||||
*/
|
*/
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Gang } from "../Gang";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IRecruitPopupProps {
|
interface IRecruitPopupProps {
|
||||||
gang: Gang;
|
open: boolean;
|
||||||
popupId: string;
|
onClose: () => void;
|
||||||
onRecruit: () => void;
|
onRecruit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RecruitPopup(props: IRecruitPopupProps): React.ReactElement {
|
export function RecruitModal(props: IRecruitPopupProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
|
|
||||||
|
const disabled = name === "" || !gang.canRecruitMember();
|
||||||
function recruit(): void {
|
function recruit(): void {
|
||||||
if (name === "") {
|
if (disabled) return;
|
||||||
dialogBoxCreate("You must enter a name for your Gang member!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!props.gang.canRecruitMember()) {
|
|
||||||
dialogBoxCreate("You cannot recruit another Gang member!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the only way this can fail is if you already
|
// At this point, the only way this can fail is if you already
|
||||||
// have a gang member with the same name
|
// have a gang member with the same name
|
||||||
if (!props.gang.recruitMember(name)) {
|
if (!gang.recruitMember(name)) {
|
||||||
dialogBoxCreate("You already have a gang member with this name!");
|
dialogBoxCreate("You already have a gang member with this name!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
props.onRecruit();
|
props.onRecruit();
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
@ -45,20 +42,23 @@ export function RecruitPopup(props: IRecruitPopupProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p className="noselect">Enter a name for your new Gang member:</p>
|
<Typography>Enter a name for your new Gang member:</Typography>
|
||||||
<br />
|
<br />
|
||||||
<input
|
<TextField
|
||||||
autoFocus
|
autoFocus
|
||||||
onKeyUp={onKeyUp}
|
onKeyUp={onKeyUp}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="text-input noselect"
|
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="unique name"
|
placeholder="unique name"
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<Button disabled={disabled} onClick={recruit}>
|
||||||
|
Recruit
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<a className="std-button" onClick={recruit}>
|
</Modal>
|
||||||
Recruit Gang Member
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -5,6 +5,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { GangMemberTasks } from "../GangMemberTasks";
|
import { GangMemberTasks } from "../GangMemberTasks";
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
member: GangMember;
|
member: GangMember;
|
||||||
@ -14,5 +15,5 @@ export function TaskDescription(props: IProps): React.ReactElement {
|
|||||||
const task = GangMemberTasks[props.member.task];
|
const task = GangMemberTasks[props.member.task];
|
||||||
const desc = task ? task.desc : GangMemberTasks["Unassigned"].desc;
|
const desc = task ? task.desc : GangMemberTasks["Unassigned"].desc;
|
||||||
|
|
||||||
return <p className="inline noselect" dangerouslySetInnerHTML={{ __html: desc }} />;
|
return <Typography dangerouslySetInnerHTML={{ __html: desc }} />;
|
||||||
}
|
}
|
||||||
|
@ -6,49 +6,50 @@ import React, { useState } from "react";
|
|||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { StatsTable } from "../../ui/React/StatsTable";
|
import { StatsTable } from "../../ui/React/StatsTable";
|
||||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||||
import { Gang } from "../Gang";
|
import { useGang } from "./Context";
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
member: GangMember;
|
member: GangMember;
|
||||||
gang: Gang;
|
|
||||||
onTaskChange: () => void;
|
onTaskChange: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TaskSelector(props: IProps): React.ReactElement {
|
export function TaskSelector(props: IProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
const [currentTask, setCurrentTask] = useState(props.member.task);
|
const [currentTask, setCurrentTask] = useState(props.member.task);
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
function onChange(event: SelectChangeEvent<string>): void {
|
||||||
const task = event.target.value;
|
const task = event.target.value;
|
||||||
props.member.assignToTask(task);
|
props.member.assignToTask(task);
|
||||||
setCurrentTask(task);
|
setCurrentTask(task);
|
||||||
props.onTaskChange();
|
props.onTaskChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
const tasks = props.gang.getAllTaskNames();
|
const tasks = gang.getAllTaskNames();
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(props.gang)} />],
|
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
|
||||||
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(props.gang))} / sec`],
|
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(gang))} / sec`],
|
||||||
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(props.gang))} / sec`],
|
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(gang))} / sec`],
|
||||||
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],
|
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<select onChange={onChange} className="dropdown noselect" value={currentTask}>
|
<Select onChange={onChange} value={currentTask}>
|
||||||
<option key={0} value={"---"}>
|
<MenuItem key={0} value={"Unassigned"}>
|
||||||
---
|
Unassigned
|
||||||
</option>
|
</MenuItem>
|
||||||
{tasks.map((task: string, i: number) => (
|
{tasks.map((task: string, i: number) => (
|
||||||
<option key={i + 1} value={task}>
|
<MenuItem key={i + 1} value={task}>
|
||||||
{task}
|
{task}
|
||||||
</option>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</Select>
|
||||||
<div>
|
|
||||||
<StatsTable rows={data} />
|
<StatsTable rows={data} />
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,59 +3,25 @@
|
|||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { AllGangs } from "../AllGangs";
|
import { AllGangs } from "../AllGangs";
|
||||||
import { Gang } from "../Gang";
|
import { Gang } from "../Gang";
|
||||||
|
import { useGang } from "./Context";
|
||||||
|
|
||||||
interface IProps {
|
import Typography from "@mui/material/Typography";
|
||||||
gang: Gang;
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||||
}
|
import Switch from "@mui/material/Switch";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
export function TerritorySubpage(props: IProps): React.ReactElement {
|
export function TerritorySubpage(): React.ReactElement {
|
||||||
function openWarfareHelp(): void {
|
const gang = useGang();
|
||||||
dialogBoxCreate(
|
const gangNames = Object.keys(AllGangs).filter((g) => g != gang.facName);
|
||||||
"This percentage represents the chance you have of " +
|
|
||||||
"'clashing' with with another gang. If you do not " +
|
|
||||||
"wish to gain/lose territory, then keep this " +
|
|
||||||
"percentage at 0% by not engaging in territory warfare.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTerritory(n: number): string {
|
|
||||||
const v = n * 100;
|
|
||||||
if (v <= 0) {
|
|
||||||
return formatNumber(0, 2);
|
|
||||||
} else if (v >= 100) {
|
|
||||||
return formatNumber(100, 2);
|
|
||||||
} else {
|
|
||||||
return formatNumber(v, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const playerPower = AllGangs[props.gang.facName].power;
|
|
||||||
function otherGangTerritory(name: string): React.ReactElement {
|
|
||||||
const power = AllGangs[name].power;
|
|
||||||
const clashVictoryChance = playerPower / (power + playerPower);
|
|
||||||
return (
|
|
||||||
<span key={name}>
|
|
||||||
<u>{name}</u>
|
|
||||||
<br />
|
|
||||||
Power: {formatNumber(power, 6)}
|
|
||||||
<br />
|
|
||||||
Territory: {formatTerritory(AllGangs[name].territory)}%<br />
|
|
||||||
Chance to win clash with this gang: {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gangNames = Object.keys(AllGangs).filter((g) => g != props.gang.facName);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: "70%" }}>
|
<>
|
||||||
<p className="noselect">
|
<Typography>
|
||||||
This page shows how much territory your Gang controls. This statistic is listed as a percentage, which
|
This page shows how much territory your Gang controls. This statistic is listed as a percentage, which
|
||||||
represents how much of the total territory you control.
|
represents how much of the total territory you control.
|
||||||
<br />
|
<br />
|
||||||
@ -76,59 +42,113 @@ export function TerritorySubpage(props: IProps): React.ReactElement {
|
|||||||
and wanted level. It is very beneficial to have high territory control.
|
and wanted level. It is very beneficial to have high territory control.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</Typography>
|
||||||
<input
|
<FormControlLabel
|
||||||
checked={props.gang.territoryWarfareEngaged}
|
control={
|
||||||
id="warfare"
|
<Switch
|
||||||
type="checkbox"
|
checked={gang.territoryWarfareEngaged}
|
||||||
style={{ display: "inline-block", margin: "2px" }}
|
onChange={(event) => (gang.territoryWarfareEngaged = event.target.checked)}
|
||||||
onChange={(event) => (props.gang.territoryWarfareEngaged = event.target.checked)}
|
|
||||||
/>
|
/>
|
||||||
<label htmlFor="warfare" className="tooltip noselect" style={{ color: "white", display: "inline-block" }}>
|
}
|
||||||
Engage in Territory Warfare
|
label={
|
||||||
<span className="tooltiptext" style={{ display: "inline-block" }}>
|
<Tooltip
|
||||||
Engaging in Territory Warfare sets your clash chance to 100%. Disengaging will cause your clash chance to
|
title={
|
||||||
gradually decrease until it reaches 0%.
|
<Typography>
|
||||||
</span>
|
Engaging in Territory Warfare sets your clash chance to 100%. Disengaging will cause your clash chance
|
||||||
</label>
|
to gradually decrease until it reaches 0%.
|
||||||
<br />
|
</Typography>
|
||||||
<p style={{ display: "inline-block" }}>
|
}
|
||||||
Territory Clash Chance: {numeralWrapper.formatPercentage(props.gang.territoryClashChance, 3)}
|
>
|
||||||
</p>
|
<Typography>Engage in Territory Warfare</Typography>
|
||||||
<div className="help-tip noselect" style={{ display: "inline-block" }} onClick={openWarfareHelp}>
|
</Tooltip>
|
||||||
?
|
}
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<input
|
|
||||||
checked={props.gang.notifyMemberDeath}
|
|
||||||
id="notify"
|
|
||||||
type="checkbox"
|
|
||||||
style={{ display: "inline-block", margin: "2px" }}
|
|
||||||
onChange={(event) => (props.gang.notifyMemberDeath = event.target.checked)}
|
|
||||||
/>
|
/>
|
||||||
<label htmlFor="warfare" className="tooltip noselect" style={{ color: "white", display: "inline-block" }}>
|
|
||||||
Notify about Gang Member Deaths
|
|
||||||
<span className="tooltiptext" style={{ display: "inline-block" }}>
|
|
||||||
If this is enabled, then you will receive a pop-up notifying you whenever one of your Gang Members dies in a
|
|
||||||
territory clash.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<br />
|
<br />
|
||||||
<fieldset style={{ display: "block", margin: "6px" }}>
|
<Box display="flex">
|
||||||
<p>
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
This percentage represents the chance you have of 'clashing' with with another gang. If you do not wish to
|
||||||
|
gain/lose territory, then keep this percentage at 0% by not engaging in territory warfare.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Territory Clash Chance: {numeralWrapper.formatPercentage(gang.territoryClashChance, 3)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<br />
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={gang.notifyMemberDeath}
|
||||||
|
onChange={(event) => (gang.notifyMemberDeath = event.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
If this is enabled, then you will receive a pop-up notifying you whenever one of your Gang Members dies
|
||||||
|
in a territory clash.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>Notify about Gang Member Deaths</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<Paper>
|
||||||
|
<Typography>
|
||||||
<b>
|
<b>
|
||||||
<u>{props.gang.facName}</u>
|
<u>{gang.facName}</u>
|
||||||
</b>
|
</b>
|
||||||
<br />
|
<br />
|
||||||
Power: {formatNumber(AllGangs[props.gang.facName].power, 6)}
|
Power: {formatNumber(AllGangs[gang.facName].power, 6)}
|
||||||
<br />
|
<br />
|
||||||
Territory: {formatTerritory(AllGangs[props.gang.facName].territory)}%
|
Territory: {formatTerritory(AllGangs[gang.facName].territory)}%
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{gangNames.map((name) => otherGangTerritory(name))}
|
</Typography>
|
||||||
</p>
|
{gangNames.map((name) => (
|
||||||
</fieldset>
|
<OtherGangTerritory key={name} name={name} />
|
||||||
</div>
|
))}
|
||||||
|
</Paper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function formatTerritory(n: number): string {
|
||||||
|
const v = n * 100;
|
||||||
|
if (v <= 0) {
|
||||||
|
return formatNumber(0, 2);
|
||||||
|
} else if (v >= 100) {
|
||||||
|
return formatNumber(100, 2);
|
||||||
|
} else {
|
||||||
|
return formatNumber(v, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITerritoryProps {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OtherGangTerritory(props: ITerritoryProps): React.ReactElement {
|
||||||
|
const gang = useGang();
|
||||||
|
const playerPower = AllGangs[gang.facName].power;
|
||||||
|
const power = AllGangs[props.name].power;
|
||||||
|
const clashVictoryChance = playerPower / (power + playerPower);
|
||||||
|
return (
|
||||||
|
<Typography>
|
||||||
|
<u>{props.name}</u>
|
||||||
|
<br />
|
||||||
|
Power: {formatNumber(power, 6)}
|
||||||
|
<br />
|
||||||
|
Territory: {formatTerritory(AllGangs[props.name].territory)}%<br />
|
||||||
|
Chance to win clash with this gang: {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { purchaseServer } from "../../Server/ServerPurchases";
|
|||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { Modal } from "../../ui/React/Modal";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { StdButton } from "../../ui/React/StdButton";
|
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* React component to create an accordion element
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
type IState = {
|
|
||||||
panelOpened: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class BBAccordion extends React.Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.handleHeaderClick = this.handleHeaderClick.bind(this);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type IPanelProps = {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): React.ReactNode {
|
|
||||||
let className = "accordion-panel";
|
|
||||||
if (typeof this.props.panelClass === "string") {
|
|
||||||
className = this.props.panelClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.props.opened) return <></>;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className} style={{ display: "block" }}>
|
|
||||||
{this.props.panelContent}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Wrapper around material-ui's Button component that styles it with
|
|
||||||
* Bitburner's UI theme
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { Button, ButtonProps } from "@mui/material";
|
|
||||||
import makeStyles from "@mui/styles/makeStyles";
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
|
|
||||||
borderRadius: 0,
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Wrapper around material-ui's Button component that styles it with
|
|
||||||
* Bitburner's UI theme
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { Paper, PaperProps } from "@mui/material";
|
|
||||||
|
|
||||||
import makeStyles from "@mui/styles/makeStyles";
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
|
||||||
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,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -16,7 +16,7 @@ export function Reputation({ reputation }: { reputation: number | string }): Rea
|
|||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<span className={classes.reputation}>
|
<span className={classes.reputation}>
|
||||||
{typeof reputation === "number" ? numeralWrapper.formatFavor(reputation) : reputation}
|
{typeof reputation === "number" ? numeralWrapper.formatReputation(reputation) : reputation}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
/**
|
|
||||||
* Basic stateless button
|
|
||||||
* Uses the 'std-button' css class
|
|
||||||
*/
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
interface IStdButtonProps {
|
|
||||||
addClasses?: string;
|
|
||||||
autoFocus?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
id?: string;
|
|
||||||
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
|
||||||
onKeyUp?: (e: React.KeyboardEvent<HTMLElement>) => any;
|
|
||||||
style?: any;
|
|
||||||
text: string | JSX.Element;
|
|
||||||
tooltip?: string | JSX.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
type IInnerHTMLMarkup = {
|
|
||||||
__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";
|
|
||||||
}
|
|
||||||
|
|
||||||
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>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={className}
|
|
||||||
id={props.id}
|
|
||||||
onClick={props.onClick}
|
|
||||||
onKeyUp={props.onKeyUp}
|
|
||||||
style={props.style}
|
|
||||||
autoFocus={props.autoFocus}
|
|
||||||
>
|
|
||||||
{props.text}
|
|
||||||
{hasTooltip && tooltip}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user