mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-23 09:42:53 +02:00
147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
import React, { useState } from "react";
|
|
import { DragDropContext, Droppable, Draggable, DropResult } from "react-beautiful-dnd";
|
|
|
|
import Box from "@mui/material/Box";
|
|
import Button from "@mui/material/Button";
|
|
import IconButton from "@mui/material/IconButton";
|
|
import TextField from "@mui/material/TextField";
|
|
import Tooltip from "@mui/material/Tooltip";
|
|
|
|
import CloseIcon from "@mui/icons-material/Close";
|
|
import SearchIcon from "@mui/icons-material/Search";
|
|
|
|
import { useBoolean, useRerender } from "../../ui/React/hooks";
|
|
import { Settings } from "../../Settings/Settings";
|
|
|
|
import { getTabId, isUnsavedFile, reorder } from "./utils";
|
|
import { OpenScript } from "./OpenScript";
|
|
import { Tab } from "./Tab";
|
|
import { SpecialServers } from "../../Server/data/SpecialServers";
|
|
|
|
const searchWidth = 180;
|
|
|
|
interface IProps {
|
|
scripts: OpenScript[];
|
|
currentScript: OpenScript | null;
|
|
|
|
onTabClick: (tabIndex: number) => void;
|
|
onTabClose: (tabIndex: number) => void;
|
|
onTabUpdate: (tabIndex: number) => void;
|
|
}
|
|
|
|
export function Tabs({ scripts, currentScript, onTabClick, onTabClose, onTabUpdate }: IProps) {
|
|
const [filter, setFilter] = useState("");
|
|
const [isSearchTooltipOpen, { on: openSearchTooltip, off: closeSearchTooltip }] = useBoolean(false);
|
|
const [searchExpanded, setSearchExpanded] = useState(false);
|
|
const rerender = useRerender();
|
|
|
|
const filteredScripts = Object.values(scripts)
|
|
.map((script, originalIndex) => ({ script, originalIndex }))
|
|
.filter(({ script }) => script.hostname.includes(filter) || script.path.includes(filter));
|
|
|
|
function onDragEnd(result: DropResult): void {
|
|
// Dropped outside of the list
|
|
if (!result.destination) return;
|
|
reorder(
|
|
scripts,
|
|
filteredScripts[result.source.index].originalIndex,
|
|
filteredScripts[result.destination.index].originalIndex,
|
|
);
|
|
rerender();
|
|
}
|
|
|
|
function handleFilterChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
setFilter(event.target.value);
|
|
}
|
|
|
|
function toggleSearch(): void {
|
|
setFilter("");
|
|
setSearchExpanded(!searchExpanded);
|
|
closeSearchTooltip();
|
|
}
|
|
|
|
function handleScroll(e: React.WheelEvent<HTMLDivElement>): void {
|
|
e.currentTarget.scrollLeft += e.deltaY;
|
|
}
|
|
|
|
return (
|
|
<Box display="flex" flexGrow="0" flexDirection="row" alignItems="center">
|
|
<Tooltip
|
|
title={"Search Open Scripts"}
|
|
open={isSearchTooltipOpen}
|
|
onOpen={openSearchTooltip}
|
|
onClose={closeSearchTooltip}
|
|
>
|
|
<span style={{ marginRight: 5 }}>
|
|
{searchExpanded ? (
|
|
<TextField
|
|
value={filter}
|
|
onChange={handleFilterChange}
|
|
autoFocus
|
|
sx={{ minWidth: searchWidth, maxWidth: searchWidth }}
|
|
InputProps={{
|
|
startAdornment: <SearchIcon />,
|
|
spellCheck: false,
|
|
endAdornment: (
|
|
<IconButton onClick={toggleSearch}>
|
|
<CloseIcon />
|
|
</IconButton>
|
|
),
|
|
}}
|
|
/>
|
|
) : (
|
|
<Button onClick={toggleSearch}>
|
|
<SearchIcon />
|
|
</Button>
|
|
)}
|
|
</span>
|
|
</Tooltip>
|
|
<DragDropContext onDragEnd={onDragEnd}>
|
|
<Droppable droppableId="tabs" direction="horizontal">
|
|
{(provided, snapshot) => (
|
|
<Box
|
|
display="flex"
|
|
flexGrow="1"
|
|
flexDirection="row"
|
|
alignItems="center"
|
|
whiteSpace="nowrap"
|
|
ref={provided.innerRef}
|
|
{...provided.droppableProps}
|
|
style={{
|
|
backgroundColor: snapshot.isDraggingOver
|
|
? Settings.theme.backgroundsecondary
|
|
: Settings.theme.backgroundprimary,
|
|
overflowX: "scroll",
|
|
}}
|
|
onWheel={handleScroll}
|
|
>
|
|
{filteredScripts.map(({ script, originalIndex }, index) => {
|
|
const { path, hostname } = script;
|
|
const isActive = currentScript?.path === script.path && currentScript.hostname === script.hostname;
|
|
const tabId = getTabId(hostname, path);
|
|
return (
|
|
<Draggable key={tabId} draggableId={tabId} index={index} disableInteractiveElementBlocking>
|
|
{(provided) => (
|
|
<Tab
|
|
provided={provided}
|
|
tabId={tabId}
|
|
isActive={isActive}
|
|
isExternal={hostname !== SpecialServers.Home}
|
|
isUnsaved={() => isUnsavedFile(scripts, index)}
|
|
onClick={() => onTabClick(originalIndex)}
|
|
onClose={() => onTabClose(originalIndex)}
|
|
onUpdate={() => onTabUpdate(originalIndex)}
|
|
/>
|
|
)}
|
|
</Draggable>
|
|
);
|
|
})}
|
|
{provided.placeholder}
|
|
</Box>
|
|
)}
|
|
</Droppable>
|
|
</DragDropContext>
|
|
</Box>
|
|
);
|
|
}
|