Add support for hiding sidebar ()

* Add support for hiding sidebar
Support cmd/ctrl + shift +s for quick hide/show

* patch sidebar padding on collapse

* update Key for sidebar setting
This commit is contained in:
Timothy Carambat 2024-12-11 17:18:38 -08:00 committed by GitHub
parent 426e2360b3
commit e4a556d551
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 162 additions and 41 deletions
frontend/src/components
Sidebar
ActiveWorkspaces/ThreadContainer/ThreadItem
SidebarToggle
index.jsx
WorkspaceChat/ChatContainer

View file

@ -41,8 +41,8 @@ export default function ThreadItem({
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH / 2 }}
className={`${
isActive
? "border-l-2 border-b-2 border-white light:border-theme-sidebar-border z-30"
: "border-l border-b border-[#6F6F71] light:border-theme-sidebar-border z-10"
? "border-l-2 border-b-2 border-white light:border-theme-sidebar-border z-[2]"
: "border-l border-b border-[#6F6F71] light:border-theme-sidebar-border z-[1]"
} h-[50%] absolute top-0 left-2 rounded-bl-lg`}
></div>
{/* Downstroke border for next item */}
@ -51,8 +51,8 @@ export default function ThreadItem({
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH / 2 }}
className={`${
idx <= activeIdx && !isActive
? "border-l-2 border-white light:border-theme-sidebar-border z-20"
: "border-l border-[#6F6F71] light:border-theme-sidebar-border z-10"
? "border-l-2 border-white light:border-theme-sidebar-border z-[2]"
: "border-l border-[#6F6F71] light:border-theme-sidebar-border z-[1]"
} h-[100%] absolute top-0 left-2`}
></div>
)}

View file

@ -0,0 +1,105 @@
import React, { useEffect, useState } from "react";
import { SidebarSimple } from "@phosphor-icons/react";
import paths from "@/utils/paths";
import { Tooltip } from "react-tooltip";
const SIDEBAR_TOGGLE_STORAGE_KEY = "anythingllm_sidebar_toggle";
/**
* Returns the previous state of the sidebar from localStorage.
* If the sidebar was closed, returns false.
* If the sidebar was open, returns true.
* If the sidebar state is not set, returns true.
* @returns {boolean}
*/
function previousSidebarState() {
const previousState = window.localStorage.getItem(SIDEBAR_TOGGLE_STORAGE_KEY);
if (previousState === "closed") return false;
return true;
}
export function useSidebarToggle() {
const [showSidebar, setShowSidebar] = useState(previousSidebarState());
const [canToggleSidebar, setCanToggleSidebar] = useState(true);
useEffect(() => {
function checkPath() {
const currentPath = window.location.pathname;
const isVisible =
currentPath === paths.home() ||
/^\/workspace\/[^\/]+$/.test(currentPath) ||
/^\/workspace\/[^\/]+\/t\/[^\/]+$/.test(currentPath);
setCanToggleSidebar(isVisible);
}
checkPath();
}, [window.location.pathname]);
useEffect(() => {
function toggleSidebar(e) {
if (!canToggleSidebar) return;
if (
(e.ctrlKey || e.metaKey) &&
e.shiftKey &&
e.key.toLowerCase() === "s"
) {
setShowSidebar((prev) => {
const newState = !prev;
window.localStorage.setItem(
SIDEBAR_TOGGLE_STORAGE_KEY,
newState ? "open" : "closed"
);
return newState;
});
}
}
window.addEventListener("keydown", toggleSidebar);
return () => {
window.removeEventListener("keydown", toggleSidebar);
};
}, []);
useEffect(() => {
window.localStorage.setItem(
SIDEBAR_TOGGLE_STORAGE_KEY,
showSidebar ? "open" : "closed"
);
}, [showSidebar]);
return { showSidebar, setShowSidebar, canToggleSidebar };
}
export function ToggleSidebarButton({ showSidebar, setShowSidebar }) {
const isMac = navigator.userAgent.includes("Mac");
const shortcut = isMac ? "⌘ + Shift + S" : "Ctrl + Shift + S";
return (
<>
<button
type="button"
className={`hidden md:block border-none bg-transparent outline-none ring-0 transition-left duration-500 ${showSidebar ? "left-[247px]" : "absolute top-[20px] left-[30px] z-10"}`}
onClick={() => setShowSidebar((prev) => !prev)}
data-tooltip-id="sidebar-toggle"
data-tooltip-content={
showSidebar
? `Hide Sidebar (${shortcut})`
: `Show Sidebar (${shortcut})`
}
aria-label={
showSidebar
? `Hide Sidebar (${shortcut})`
: `Show Sidebar (${shortcut})`
}
>
<SidebarSimple
className="text-theme-text-secondary hover:text-theme-text-primary"
size={24}
/>
</button>
<Tooltip
id="sidebar-toggle"
place="top"
delayShow={300}
className="tooltip !text-xs z-99"
/>
</>
);
}

View file

@ -11,11 +11,13 @@ import SettingsButton from "../SettingsButton";
import { Link } from "react-router-dom";
import paths from "@/utils/paths";
import { useTranslation } from "react-i18next";
import { useSidebarToggle, ToggleSidebarButton } from "./SidebarToggle";
export default function Sidebar() {
const { user } = useUser();
const { logo } = useLogo();
const sidebarRef = useRef(null);
const { showSidebar, setShowSidebar, canToggleSidebar } = useSidebarToggle();
const {
showing: showingNewWsModal,
showModal: showNewWsModal,
@ -24,50 +26,64 @@ export default function Sidebar() {
const { t } = useTranslation();
return (
<div>
<Link
to={paths.home()}
className="flex shrink-0 max-w-[55%] items-center justify-start mx-[38px] my-[18px]"
aria-label="Home"
>
<img
src={logo}
alt="Logo"
className="rounded max-h-[24px] object-contain"
/>
</Link>
<>
<div
ref={sidebarRef}
className="relative m-[16px] rounded-[16px] bg-theme-bg-sidebar border-[2px] border-theme-sidebar-border light:border-none min-w-[250px] p-[10px] h-[calc(100%-76px)]"
style={{
width: showSidebar ? "292px" : "0px",
paddingLeft: showSidebar ? "0px" : "16px",
}}
className="transition-all duration-500"
>
<div className="flex flex-col h-full overflow-x-hidden">
<div className="flex-grow flex flex-col min-w-[235px]">
<div className="relative h-[calc(100%-60px)] flex flex-col w-full justify-between pt-[10px] overflow-y-scroll no-scroll">
<div className="flex flex-col gap-y-2 pb-[60px] overflow-y-scroll no-scroll">
<div className="flex gap-x-2 items-center justify-between">
{(!user || user?.role !== "default") && (
<button
onClick={showNewWsModal}
className="light:bg-[#C2E7FE] light:hover:bg-[#7CD4FD] flex flex-grow w-[75%] h-[44px] gap-x-2 py-[5px] px-2.5 mb-2 bg-white rounded-[8px] text-sidebar justify-center items-center hover:bg-opacity-80 transition-all duration-300"
>
<Plus size={18} weight="bold" />
<p className="text-sidebar text-sm font-semibold">
{t("new-workspace.title")}
</p>
</button>
)}
<div className="flex shrink-0 w-full justify-center my-[18px]">
<div className="flex justify-between w-[250px] min-w-[250px]">
<Link to={paths.home()} aria-label="Home">
<img
src={logo}
alt="Logo"
className={`rounded max-h-[24px] object-contain transition-opacity duration-500 ${showSidebar ? "opacity-100" : "opacity-0"}`}
/>
</Link>
{canToggleSidebar && (
<ToggleSidebarButton
showSidebar={showSidebar}
setShowSidebar={setShowSidebar}
/>
)}
</div>
</div>
<div
ref={sidebarRef}
className="relative m-[16px] rounded-[16px] bg-theme-bg-sidebar border-[2px] border-theme-sidebar-border light:border-none min-w-[250px] p-[10px] h-[calc(100%-76px)]"
>
<div className="flex flex-col h-full overflow-x-hidden">
<div className="flex-grow flex flex-col min-w-[235px]">
<div className="relative h-[calc(100%-60px)] flex flex-col w-full justify-between pt-[10px] overflow-y-scroll no-scroll">
<div className="flex flex-col gap-y-2 pb-[60px] overflow-y-scroll no-scroll">
<div className="flex gap-x-2 items-center justify-between">
{(!user || user?.role !== "default") && (
<button
onClick={showNewWsModal}
className="light:bg-[#C2E7FE] light:hover:bg-[#7CD4FD] flex flex-grow w-[75%] h-[44px] gap-x-2 py-[5px] px-2.5 mb-2 bg-white rounded-[8px] text-sidebar justify-center items-center hover:bg-opacity-80 transition-all duration-300"
>
<Plus size={18} weight="bold" />
<p className="text-sidebar text-sm font-semibold">
{t("new-workspace.title")}
</p>
</button>
)}
</div>
<ActiveWorkspaces />
</div>
<ActiveWorkspaces />
</div>
</div>
<div className="absolute bottom-0 left-0 right-0 pt-4 pb-3 rounded-b-[16px] bg-theme-bg-sidebar bg-opacity-80 backdrop-filter backdrop-blur-md z-10">
<Footer />
<div className="absolute bottom-0 left-0 right-0 pt-4 pb-3 rounded-b-[16px] bg-theme-bg-sidebar bg-opacity-80 backdrop-filter backdrop-blur-md z-1">
<Footer />
</div>
</div>
</div>
</div>
{showingNewWsModal && <NewWorkspaceModal hideModal={hideNewWsModal} />}
</div>
{showingNewWsModal && <NewWorkspaceModal hideModal={hideNewWsModal} />}
</div>
</>
);
}

View file

@ -264,7 +264,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
return (
<div
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-theme-bg-secondary w-full h-full overflow-y-scroll no-scroll"
className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-theme-bg-secondary w-full h-full overflow-y-scroll no-scroll z-[2]"
>
{isMobile && <SidebarMobileHeader />}
<DnDFileUploaderWrapper>