Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions src/components/sidebar/PathSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ import { SavingState } from "../../document/UIStateStore";
import SaveInProgress from "../../assets/SaveInProgress";
import { NameIssue } from "../../document/path/NameIsIdentifier";

type Props = object;

type Props = {
searchQuery: string;
regexMode: boolean;
sortAlphabetical: boolean;
};
type State = object;

type OptionProps = { uuid: string; selected: boolean };
Expand Down Expand Up @@ -311,15 +314,34 @@ class PathSelectorOption extends Component<OptionProps, OptionState> {
}

class PathSelector extends Component<Props, State> {
state = {};

Option = observer(PathSelectorOption);
render() {
let regex: RegExp | null = null;
if (this.props.regexMode) {
try {
regex = new RegExp(this.props.searchQuery, "i");
} catch {
toast.error("Invalid regular expression: " + this.props.searchQuery);
}
}
const activePath = doc.pathlist.activePathUUID;
const filteredPathEntries = Array.from(doc.pathlist.paths.entries()).filter(
([, path]) => {
const query = this.props.searchQuery.trim();
if (!query) return true;
return regex == null
? path.name.toLowerCase().includes(query.toLowerCase())
: regex.test(path.name);
}
);
if (this.props.sortAlphabetical) {
filteredPathEntries.sort((a, b) => a[1].name.localeCompare(b[1].name));
}

return (
<div>
<div className={styles.WaypointList}>
{Array.from(doc.pathlist.paths.keys()).map((uuid) => (
{filteredPathEntries.map(([uuid]) => (
<this.Option
uuid={uuid}
key={uuid}
Expand Down
16 changes: 16 additions & 0 deletions src/components/sidebar/Sidebar.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.Container {
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-start;
Expand Down Expand Up @@ -112,3 +113,18 @@
.SidebarItem .SidebarRightIcon {
padding-right: 8px;
}

.ResizeHandle {
position: absolute;
top: 0;
right: -2px;
width: 4px;
height: 100%;
cursor: col-resize;
z-index: 1100;
transition: background-color 0.2s ease;
}
.ResizeHandle:hover,
.ResizeHandle:active {
background-color: var(--accent-purple);
}
170 changes: 158 additions & 12 deletions src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Component } from "react";
import { doc, uiState } from "../../document/DocumentManager";
import { observer } from "mobx-react";
import styles from "./Sidebar.module.css";
import { Divider, IconButton, Tooltip } from "@mui/material";
import { Divider, IconButton, TextField, Tooltip } from "@mui/material";
import WaypointList from "./WaypointList";
import PathSelector from "./PathSelector";
import MenuIcon from "@mui/icons-material/Menu";
Expand All @@ -11,7 +11,10 @@ import {
Redo,
ShapeLine,
Polyline,
Undo
Undo,
Search,
Clear,
Abc
} from "@mui/icons-material";
import Add from "@mui/icons-material/Add";
import SidebarConstraint from "./SidebarConstraint";
Expand All @@ -21,19 +24,141 @@ import { IEventMarkerStore } from "../../document/EventMarkerStore";
import ProjectSaveStatusIndicator from "./ProjectSaveStatusIndicator";

type Props = object;

type State = object;

class TrajectorySearch extends Component<Props, State> {
render() {
const { trajSearchQuery, setTrajSearchQuery } = uiState;

return (
<div
style={{
position: "sticky",
top: "-8px",
zIndex: 10,
backgroundColor: "var(--background-dark-gray)",
paddingInline: "8px",
paddingBottom: "8px",
paddingTop: "8px"
}}
>
<TextField
variant="outlined"
size="small"
placeholder="Search paths..."
value={trajSearchQuery}
onChange={(e) => setTrajSearchQuery(e.target.value)}
fullWidth
slotProps={{
input: {
startAdornment: (
<Search
sx={{
color: "gray",
marginRight: "4px",
fontSize: "20px"
}}
/>
),
endAdornment: (
<>
<Tooltip
disableInteractive
title={
uiState.trajSearchRegex
? "Disable regex search"
: "Enable regex search"
}
>
<IconButton
size="small"
onClick={uiState.toggleTrajSearchRegex}
sx={{
borderRadius: "3px",
fontFamily: "monospace",
fontSize: "11px",
fontWeight: "bold",
lineHeight: 1,
color: uiState.trajSearchRegex
? "var(--accent-purple)"
: "white",
border: uiState.trajSearchRegex
? "1px solid var(--accent-purple)"
: "1px solid transparent"
}}
>
Re
</IconButton>
</Tooltip>
{trajSearchQuery && (
<IconButton
size="small"
onClick={() => setTrajSearchQuery("")}
>
<Clear sx={{ fontSize: "18px", color: "white" }} />
</IconButton>
)}
</>
),
style: {
color: "white",
backgroundColor: "var(--background-light-gray)",
borderRadius: "4px",
height: "32px",
fontSize: "14px"
}
}
}}
sx={{
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "transparent"
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: "var(--divider-gray)"
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: "var(--accent-purple)"
}
}}
/>
</div>
);
}
}

class Sidebar extends Component<Props, State> {
state = {};
constructor(props: Props) {
super(props);
}

startResize = (e: React.MouseEvent) => {
e.preventDefault();
document.addEventListener("mousemove", this.resize);
document.addEventListener("mouseup", this.stopResize);
};

resize = (e: MouseEvent) => {
const newWidth = Math.max(260, Math.min(560, e.clientX));
document.documentElement.style.setProperty(
"--sidebar-width",
`${newWidth}px`
);
};

stopResize = () => {
document.removeEventListener("mousemove", this.resize);
document.removeEventListener("mouseup", this.stopResize);
};

componentWillUnmount() {
this.stopResize();
}

render() {
const { toggleMainMenu } = uiState;
return (
<div className={styles.Container}>
<div onMouseDown={this.startResize} className={styles.ResizeHandle} />
<div
style={{
flexShrink: 0,
Expand All @@ -49,11 +174,7 @@ class Sidebar extends Component<Props, State> {
>
<span>
<Tooltip disableInteractive title="Main Menu">
<IconButton
onClick={() => {
toggleMainMenu();
}}
>
<IconButton onClick={uiState.toggleMainMenu}>
<MenuIcon></MenuIcon>
</IconButton>
</Tooltip>
Expand Down Expand Up @@ -92,9 +213,28 @@ class Sidebar extends Component<Props, State> {
</div>
<div
className={styles.SidebarHeading}
style={{ gridTemplateColumns: "auto 33.6px 33.6px 33.6px 33.6px" }}
style={{
gridTemplateColumns: "auto 33.6px 33.6px 33.6px 33.6px 33.6px"
}}
>
PATHS
<Tooltip disableInteractive title="Sort by Alphabetical Order">
<span>
<IconButton
size="small"
color="default"
style={{ float: "right" }}
sx={{
color: uiState.sortAlphabetical
? "var(--accent-purple)"
: "white"
}}
onClick={uiState.toggleSortAlphabetical}
>
<Abc />
</IconButton>
</span>
</Tooltip>
<Tooltip disableInteractive title="Generate All">
<span>
<IconButton
Expand Down Expand Up @@ -155,12 +295,18 @@ class Sidebar extends Component<Props, State> {
</IconButton>
</Tooltip>
</div>
<Divider></Divider>
<Divider />
<TrajectorySearch />
<Divider />
<div
className={styles.Sidebar}
style={{ maxHeight: "300px", minHeight: "50px" }}
>
<PathSelector></PathSelector>
<PathSelector
searchQuery={uiState.trajSearchQuery ?? ""}
regexMode={uiState.trajSearchRegex}
sortAlphabetical={uiState.sortAlphabetical}
></PathSelector>
</div>
<Divider></Divider>
<div className={styles.SidebarHeading}>FEATURES</div>
Expand Down
15 changes: 14 additions & 1 deletion src/document/UIStateStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ export const UIStateStore = types

contextMenuSelectedWaypoint: types.maybe(types.number),
contextMenuWaypointType: types.maybe(types.number),
contextMenuMouseSelection: types.maybe(types.array(types.number)) // [clientX, clientY] from `MouseEvent`
contextMenuMouseSelection: types.maybe(types.array(types.number)), // [clientX, clientY] from `MouseEvent`

trajSearchQuery: types.maybe(types.string),
trajSearchRegex: false,
sortAlphabetical: false
})
.views((self: any) => {
return {
Expand Down Expand Up @@ -210,6 +214,15 @@ export const UIStateStore = types
self.contextMenuMouseSelection = mouseSelection
? [mouseSelection.clientX, mouseSelection.clientY]
: undefined;
},
setTrajSearchQuery(query: string) {
self.trajSearchQuery = query;
},
toggleTrajSearchRegex() {
self.trajSearchRegex = !self.trajSearchRegex;
},
toggleSortAlphabetical() {
self.sortAlphabetical = !self.sortAlphabetical;
}
}));
export type IUIStateStore = Instance<typeof UIStateStore>;
Loading