Skip to content

Commit

Permalink
Merge pull request #67 from SeanCassiere/SeanCassiere/feat-add-sideba…
Browse files Browse the repository at this point in the history
…r-toggle-func

feat: add toggle sidebar functionality
  • Loading branch information
iway1 authored Apr 19, 2023
2 parents 2467157 + ffef2df commit 8e99d0c
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 10 deletions.
8 changes: 6 additions & 2 deletions packages/trpc-panel/src/react-app/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
HeadersContextProvider,
useHeaders,
} from "@src/react-app/components/contexts/HeadersContext";
import { useLocalStorage } from "@src/react-app/components/hooks/useLocalStorage";
import { HeadersPopup } from "@src/react-app/components/HeadersPopup";
import { Toaster } from "react-hot-toast";
import { SiteNavigationContextProvider } from "@src/react-app/components/contexts/SiteNavigationContext";
Expand Down Expand Up @@ -80,11 +81,14 @@ function ClientProviders({
}

function AppInnards({ rootRouter }: { rootRouter: ParsedRouter }) {
const [sidebarOpen, setSidebarOpen] = useState(true);
const [sidebarOpen, setSidebarOpen] = useLocalStorage(
"trpc-panel.show-minimap",
true
);

return (
<div className="flex flex-col flex-1 relative">
<TopBar />
<TopBar open={sidebarOpen} setOpen={setSidebarOpen} />
<div className="flex flex-row flex-1 bg-mainBackground">
<SideNav
rootRouter={rootRouter}
Expand Down
4 changes: 3 additions & 1 deletion packages/trpc-panel/src/react-app/components/Chevron.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function Chevron({
direction,
}: {
className?: string;
direction: "up" | "down" | "right";
direction: "up" | "down" | "right" | "left";
}) {
return (
<ChevronIcon
Expand All @@ -21,6 +21,8 @@ export function Chevron({
return "-rotate-90";
case "right":
return "rotate-180";
case "left":
return "";
}
})()}`
}
Expand Down
5 changes: 3 additions & 2 deletions packages/trpc-panel/src/react-app/components/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import { colorSchemeForNode } from "@src/react-app/components/style-utils";
import { ItemTypeIcon } from "@src/react-app/components/ItemTypeIcon";
export function SideNav({
rootRouter,
open,
}: // setOpen,
{
open: boolean;
rootRouter: ParsedRouter;
setOpen: (value: boolean) => void;
}) {
return (
return open ? (
<div
style={{ maxHeight: "calc(100vh - 4rem)" }}
className="min-w-[16rem] overflow-scroll shadow-sm flex-col flex items-start p-2 pr-4 space-y-2 bg-actuallyWhite border-r-2 border-r-panelBorder"
>
<SideNavItem node={rootRouter} path={[]} />
</div>
);
) : null;
}

function SideNavItem({
Expand Down
32 changes: 27 additions & 5 deletions packages/trpc-panel/src/react-app/components/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,39 @@ import { useHeadersContext } from "@src/react-app/components/contexts/HeadersCon
import MailLockIcon from "@mui/icons-material/MailLockOutlined";
import { LogoSvg } from "@src/react-app/components/LogoSvg";
import { useIsMac } from "@src/react-app/components/hooks/useIsMac";
import { Chevron } from "@src/react-app/components/Chevron";
import Search from "@mui/icons-material/Search";
import { useSearch } from "@src/react-app/components/contexts/SearchStore";

export function TopBar() {
export function TopBar({
open,
setOpen,
}: {
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const { setHeadersPopupShown } = useHeadersContext();
return (
<div className="w-full px-4 pr-8 flex flex-row justify-between items-center position-fixed left-0 h-16 right-0 top-0 bg-gray-50 drop-shadow-sm bg-actuallyWhite border-b border-b-panelBorder">
<span className="flex flex-row items-center text-lg font-bold font-mono">
<LogoSvg className="rounded-lg w-10 h-10 mr-2" />
tRPC.panel()
</span>
<div className="flex flex-row items-center gap-4">
<button
type="button"
role="button"
onClick={() => setOpen((prev) => !prev)}
aria-label="Toggle sidebar"
aria-pressed={open}
>
{open ? (
<Chevron className="w-4 h-4" direction="left" />
) : (
<Chevron className="w-4 h-4" direction="right" />
)}
</button>
<span className="flex flex-row items-center text-lg font-bold font-mono">
<LogoSvg className="rounded-lg w-10 h-10 mr-2" />
tRPC.panel()
</span>
</div>
<RouterSearchTooltip />
<button
onClick={() => setHeadersPopupShown(true)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// a stripped down version of https://usehooks-ts.com/react-hook/use-local-storage
import {
useCallback,
useEffect,
useState,
type Dispatch,
type SetStateAction,
} from "react";

type SetValue<T> = Dispatch<SetStateAction<T>>;

export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, SetValue<T>] {
// Get from local storage then
// parse stored json or return initialValue
const readValue = useCallback((): T => {
// Prevent build error "window is undefined" but keeps working
if (typeof window === "undefined") {
return initialValue;
}

try {
const item = window.localStorage.getItem(key);
return item ? (parseJSON(item) as T) : initialValue;
} catch (error) {
console.warn(
`tRPC-Panel.useLocalStorage: Error reading localStorage key “${key}”:`,
error
);
return initialValue;
}
}, [initialValue, key]);

// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState<T>(readValue);

// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue: SetValue<T> = useCallback(
(value) => {
// Prevent build error "window is undefined" but keeps working
if (typeof window === "undefined") {
console.warn(
`tRPC-Panel.useLocalStorage: Tried setting localStorage key “${key}” even though environment is not a client`
);
}

try {
// Allow value to be a function so we have the same API as useState
const newValue = value instanceof Function ? value(storedValue) : value;

// Save to local storage
window.localStorage.setItem(key, JSON.stringify(newValue));

// Save state
setStoredValue(newValue);
} catch (error) {
console.warn(
`tRPC-Panel.useLocalStorage: Error setting localStorage key “${key}”:`,
error
);
}
},
[storedValue]
);

useEffect(() => {
setStoredValue(readValue());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return [storedValue, setValue];
}

// A wrapper for "JSON.parse()"" to support "undefined" value
function parseJSON<T>(value: string | null): T | undefined {
try {
return value === "undefined" ? undefined : JSON.parse(value ?? "");
} catch {
console.log("tRPC-Panel.useLocalStorage.parseJSON: parsing error on", {
value,
});
return undefined;
}
}

0 comments on commit 8e99d0c

Please sign in to comment.