Sidebar tweaks

This commit is contained in:
omuretsu 2022-12-04 23:04:33 -05:00
parent a46d34bd60
commit 2489ec43fe
4 changed files with 49 additions and 81 deletions

1
.gitignore vendored

@ -22,3 +22,4 @@ Netburner.txt
.idea/ .idea/
*.bundle.* *.bundle.*
index.html index.html
tsdoc-metadata.json

@ -17,7 +17,7 @@ interface IProps {
clickPage: (page: Page) => void; clickPage: (page: Page) => void;
flash: Page | null; flash: Page | null;
items: (IItemProps | boolean)[]; items: (IItemProps | boolean)[];
icon: React.ReactElement; icon: React.ReactElement["type"];
sidebarOpen: boolean; sidebarOpen: boolean;
classes: any; classes: any;
} }
@ -44,10 +44,7 @@ function getClickFn(toWrap: (page: Page) => void, page: Page) {
// This can't be usefully memoized, because props.items is a new array every time. // This can't be usefully memoized, because props.items is a new array every time.
export function SidebarAccordion(props: IProps): React.ReactElement { export function SidebarAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
// Obnoxious, because we can't modify props at all.
const li_classes = useMemo(() => ({ root: props.classes.listitem }), [props.classes.listitem]); const li_classes = useMemo(() => ({ root: props.classes.listitem }), [props.classes.listitem]);
const icon = Object.assign({}, props.icon);
icon.props = Object.assign({ color: "primary" }, icon.props);
// Explicitily useMemo() to save rerendering deep chunks of this tree. // Explicitily useMemo() to save rerendering deep chunks of this tree.
// memo() can't be (easily) used on components like <List>, because the // memo() can't be (easily) used on components like <List>, because the
@ -58,13 +55,15 @@ export function SidebarAccordion(props: IProps): React.ReactElement {
() => ( () => (
<ListItem classes={li_classes} button onClick={() => setOpen((open) => !open)}> <ListItem classes={li_classes} button onClick={() => setOpen((open) => !open)}>
<ListItemIcon> <ListItemIcon>
<Tooltip title={!props.sidebarOpen ? props.key_ : ""} children={icon} /> <Tooltip title={!props.sidebarOpen ? props.key_ : ""}>
<props.icon color={"primary"} />
</Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText primary={<Typography>{props.key_}</Typography>} /> <ListItemText primary={<Typography>{props.key_}</Typography>} />
{open ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />} {open ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />}
</ListItem> </ListItem>
), ),
[li_classes, props.sidebarOpen, props.key_, open, props.icon.type], [li_classes, props.sidebarOpen, props.key_, open, props.icon],
)} )}
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={open} timeout="auto" unmountOnExit>
{props.items.map((x) => { {props.items.map((x) => {

@ -10,7 +10,7 @@ import type { Page } from "../../ui/Router";
export interface ICreateProps { export interface ICreateProps {
key_: Page; key_: Page;
icon: React.ReactElement; icon: React.ReactElement["type"];
count?: number; count?: number;
active?: boolean; active?: boolean;
} }
@ -23,15 +23,7 @@ export interface IProps extends ICreateProps {
} }
export const SidebarItem = memo(function (props: IProps): React.ReactElement { export const SidebarItem = memo(function (props: IProps): React.ReactElement {
// Use icon as a template. (We can't modify props) const color = props.flash ? "error" : props.active ? "primary" : "secondary";
const icon: React.ReactElement = {
type: props.icon.type,
key: props.icon.key,
props: {
color: props.flash ? "error" : !props.active ? "secondary" : "primary",
...props.icon.props,
},
};
return ( return (
<ListItem <ListItem
classes={{ root: props.classes.listitem }} classes={{ root: props.classes.listitem }}
@ -42,11 +34,13 @@ export const SidebarItem = memo(function (props: IProps): React.ReactElement {
> >
<ListItemIcon> <ListItemIcon>
<Badge badgeContent={(props.count ?? 0) > 0 ? props.count : undefined} color="error"> <Badge badgeContent={(props.count ?? 0) > 0 ? props.count : undefined} color="error">
<Tooltip title={!props.sidebarOpen ? props.key_ : ""} children={icon} /> <Tooltip title={!props.sidebarOpen ? props.key_ : ""}>
<props.icon color={color} />
</Tooltip>
</Badge> </Badge>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={props.flash ? "error" : !props.active ? "secondary" : "primary"} children={props.key_} /> <Typography color={color} children={props.key_} />
</ListItemText> </ListItemText>
</ListItem> </ListItem>
); );

@ -55,38 +55,9 @@ import { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
import { hash } from "../../hash/hash"; import { hash } from "../../hash/hash";
import { Locations } from "../../Locations/Locations"; import { Locations } from "../../Locations/Locations";
// All icon instances need to be constant, so they have stable object identity. const RotatedDoubleArrowIcon = (props: { color: "primary" | "secondary" | "error" }) => (
// Otherwise, the memoization of all the higher-level components doesn't work. <DoubleArrowIcon color={props.color} style={{ transform: "rotate(-90deg)" }} />
const computerIcon = <ComputerIcon />; );
const lastPageIcon = <LastPageIcon />;
const createIcon = <CreateIcon />;
const storageIcon = <StorageIcon />;
const bugReportIcon = <BugReportIcon />;
const equalizerIcon = <EqualizerIcon />;
const contactsIcon = <ContactsIcon />;
const doubleArrowIcon = <DoubleArrowIcon style={{ transform: "rotate(-90deg)" }} />;
const accountTreeIcon = <AccountTreeIcon />;
const peopleAltIcon = <PeopleAltIcon />;
const locationCityIcon = <LocationCityIcon />;
const airplanemodeActiveIcon = <AirplanemodeActiveIcon />;
const workIcon = <WorkIcon />;
const trendingUpIcon = <TrendingUpIcon />;
const formatBoldIcon = <FormatBoldIcon />;
const businessIcon = <BusinessIcon />;
const sportsMmaIcon = <SportsMmaIcon />;
const checkIcon = <CheckIcon />;
const helpIcon = <HelpIcon />;
const settingsIcon = <SettingsIcon />;
const developerBoardIcon = <DeveloperBoardIcon />;
const emojiEventsIcon = <EmojiEventsIcon />;
const accountBoxIcon = <AccountBoxIcon />;
const publicIcon = <PublicIcon />;
const liveHelpIcon = <LiveHelpIcon />;
const chevronLeftIcon = <ChevronLeftIcon color="primary" />;
const chevronRightIcon = <ChevronRightIcon color="primary" />;
// Use constant Dividers just for performance
const divider = <Divider />;
const openedMixin = (theme: Theme): CSSObject => ({ const openedMixin = (theme: Theme): CSSObject => ({
width: theme.spacing(31), width: theme.spacing(31),
@ -295,6 +266,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
return !old; return !old;
}); });
const li_classes = useMemo(() => ({ root: classes.listitem }), [classes.listitem]); const li_classes = useMemo(() => ({ root: classes.listitem }), [classes.listitem]);
const ChevronOpenClose = open ? ChevronLeftIcon : ChevronRightIcon;
// Explicitily useMemo() to save rerendering deep chunks of this tree. // Explicitily useMemo() to save rerendering deep chunks of this tree.
// memo() can't be (easily) used on components like <List>, because the // memo() can't be (easily) used on components like <List>, because the
@ -304,7 +276,9 @@ export function SidebarRoot(props: IProps): React.ReactElement {
{useMemo( {useMemo(
() => ( () => (
<ListItem classes={li_classes} button onClick={toggleDrawer}> <ListItem classes={li_classes} button onClick={toggleDrawer}>
<ListItemIcon>{!open ? chevronRightIcon : chevronLeftIcon}</ListItemIcon> <ListItemIcon>
<ChevronOpenClose color={"primary"} />
</ListItemIcon>
<ListItemText <ListItemText
primary={ primary={
<Tooltip title={hash()}> <Tooltip title={hash()}>
@ -316,88 +290,88 @@ export function SidebarRoot(props: IProps): React.ReactElement {
), ),
[li_classes, open], [li_classes, open],
)} )}
{divider} <Divider />
<List> <List>
<SidebarAccordion <SidebarAccordion
key_="Hacking" key_="Hacking"
page={props.page} page={props.page}
clickPage={clickPage} clickPage={clickPage}
flash={flash} flash={flash}
icon={computerIcon} icon={ComputerIcon}
sidebarOpen={open} sidebarOpen={open}
classes={classes} classes={classes}
items={[ items={[
{ key_: Page.Terminal, icon: lastPageIcon }, { key_: Page.Terminal, icon: LastPageIcon },
{ key_: Page.ScriptEditor, icon: createIcon }, { key_: Page.ScriptEditor, icon: CreateIcon },
{ key_: Page.ActiveScripts, icon: storageIcon }, { key_: Page.ActiveScripts, icon: StorageIcon },
{ key_: Page.CreateProgram, icon: bugReportIcon, count: programCount }, { key_: Page.CreateProgram, icon: BugReportIcon, count: programCount },
canStaneksGift && { key_: Page.StaneksGift, icon: developerBoardIcon }, canStaneksGift && { key_: Page.StaneksGift, icon: DeveloperBoardIcon },
]} ]}
/> />
{divider} <Divider />
<SidebarAccordion <SidebarAccordion
key_="Character" key_="Character"
page={props.page} page={props.page}
clickPage={clickPage} clickPage={clickPage}
flash={flash} flash={flash}
icon={accountBoxIcon} icon={AccountBoxIcon}
sidebarOpen={open} sidebarOpen={open}
classes={classes} classes={classes}
items={[ items={[
{ key_: Page.Stats, icon: equalizerIcon }, { key_: Page.Stats, icon: EqualizerIcon },
canOpenFactions && { canOpenFactions && {
key_: Page.Factions, key_: Page.Factions,
icon: contactsIcon, icon: ContactsIcon,
active: [Page.Factions as Page, Page.Faction].includes(props.page), active: [Page.Factions as Page, Page.Faction].includes(props.page),
count: invitationsCount, count: invitationsCount,
}, },
canOpenAugmentations && { canOpenAugmentations && {
key_: Page.Augmentations, key_: Page.Augmentations,
icon: doubleArrowIcon, icon: RotatedDoubleArrowIcon,
count: augmentationCount, count: augmentationCount,
}, },
{ key_: Page.Hacknet, icon: accountTreeIcon }, { key_: Page.Hacknet, icon: AccountTreeIcon },
canOpenSleeves && { key_: Page.Sleeves, icon: peopleAltIcon }, canOpenSleeves && { key_: Page.Sleeves, icon: PeopleAltIcon },
]} ]}
/> />
{divider} <Divider />
<SidebarAccordion <SidebarAccordion
key_="World" key_="World"
page={props.page} page={props.page}
clickPage={clickPage} clickPage={clickPage}
flash={flash} flash={flash}
icon={publicIcon} icon={PublicIcon}
sidebarOpen={open} sidebarOpen={open}
classes={classes} classes={classes}
items={[ items={[
{ {
key_: Page.City, key_: Page.City,
icon: locationCityIcon, icon: LocationCityIcon,
active: [Page.City as Page, Page.Grafting, Page.Location].includes(props.page), active: [Page.City as Page, Page.Grafting, Page.Location].includes(props.page),
}, },
{ key_: Page.Travel, icon: airplanemodeActiveIcon }, { key_: Page.Travel, icon: AirplanemodeActiveIcon },
canJob && { key_: Page.Job, icon: workIcon }, canJob && { key_: Page.Job, icon: WorkIcon },
canStockMarket && { key_: Page.StockMarket, icon: trendingUpIcon }, canStockMarket && { key_: Page.StockMarket, icon: TrendingUpIcon },
canBladeburner && { key_: Page.Bladeburner, icon: formatBoldIcon }, canBladeburner && { key_: Page.Bladeburner, icon: FormatBoldIcon },
canCorporation && { key_: Page.Corporation, icon: businessIcon }, canCorporation && { key_: Page.Corporation, icon: BusinessIcon },
canGang && { key_: Page.Gang, icon: sportsMmaIcon }, canGang && { key_: Page.Gang, icon: SportsMmaIcon },
]} ]}
/> />
{divider} <Divider />
<SidebarAccordion <SidebarAccordion
key_="Help" key_="Help"
page={props.page} page={props.page}
clickPage={clickPage} clickPage={clickPage}
flash={flash} flash={flash}
icon={liveHelpIcon} icon={LiveHelpIcon}
sidebarOpen={open} sidebarOpen={open}
classes={classes} classes={classes}
items={[ items={[
{ key_: Page.Milestones, icon: checkIcon }, { key_: Page.Milestones, icon: CheckIcon },
{ key_: Page.Tutorial, icon: helpIcon }, { key_: Page.Tutorial, icon: HelpIcon },
{ key_: Page.Achievements, icon: emojiEventsIcon }, { key_: Page.Achievements, icon: EmojiEventsIcon },
{ key_: Page.Options, icon: settingsIcon }, { key_: Page.Options, icon: SettingsIcon },
process.env.NODE_ENV === "development" && { key_: Page.DevMenu, icon: developerBoardIcon }, process.env.NODE_ENV === "development" && { key_: Page.DevMenu, icon: DeveloperBoardIcon },
]} ]}
/> />
</List> </List>