Skip to content

Commit

Permalink
breakout menu option to its own folder
Browse files Browse the repository at this point in the history
  • Loading branch information
timothycarambat committed Jul 10, 2024
2 parents 5e422f7 + deb237f commit e0d9221
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 146 deletions.
143 changes: 143 additions & 0 deletions frontend/src/components/SettingsSidebar/MenuOption/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React, { useEffect, useState } from "react";
import { CaretRight } from "@phosphor-icons/react";
import { Link } from "react-router-dom";

export default function MenuOption({
btnText,
icon,
href,
childOptions = [],
flex = false,
user = null,
roles = [],
hidden = false,
isChild = false,
}) {
const storageKey = generateStorageKey({ key: btnText });
const location = window.location.pathname;
const hasChildren = childOptions.length > 0;
const hasVisibleChildren = hasChildren
? childOptions.some((opt) =>
isVisible({ roles: opt.roles, user, flex: opt.flex })
)
: false;
const [isExpanded, setIsExpanded] = useState(() => {
if (hasVisibleChildren) {
const storedValue = localStorage.getItem(storageKey);
if (storedValue !== null) {
return JSON.parse(storedValue);
}
return childOptions.some((child) => child.href === location);
}
return false;
});

useEffect(() => {
if (hasVisibleChildren) {
const shouldExpand = childOptions.some(
(child) => child.href === location
);
if (shouldExpand && !isExpanded) {
setIsExpanded(true);
localStorage.setItem(storageKey, JSON.stringify(true));
}
}
}, [location]);

if (hidden) return null;

// If this option is a parent level option
if (!isChild) {
// and has no children then use its flex props and roles prop directly
if (!hasChildren) {
if (!flex && !roles.includes(user?.role)) return null;
if (flex && !!user && !roles.includes(user?.role)) return null;
}

// if has children and no visible children - remove it.
if (hasChildren && !hasVisibleChildren) return null;
} else {
// is a child so we use it's permissions
if (!flex && !roles.includes(user?.role)) return null;
if (flex && !!user && !roles.includes(user?.role)) return null;
}

const isActive = hasChildren
? (!isExpanded && childOptions.some((child) => child.href === location)) ||
location === href
: location === href;

const handleClick = (e) => {
if (hasChildren) {
e.preventDefault();
const newExpandedState = !isExpanded;
setIsExpanded(newExpandedState);
localStorage.setItem(storageKey, JSON.stringify(newExpandedState));
}
};

return (
<div>
<div
className={`
flex items-center justify-between w-full
transition-all duration-300
rounded-[6px]
${isActive
? "bg-white/5 font-medium border-outline"
: "hover:bg-white/5"
}
`}
>
<Link
to={href}
className={`flex flex-grow items-center px-[12px] h-[32px] font-medium ${isChild ? "text-white/70 hover:text-white" : "text-white"
}`}
onClick={hasChildren ? handleClick : undefined}
>
{icon}
<p
className={`${isChild ? "text-xs" : "text-sm"
} leading-loose whitespace-nowrap overflow-hidden ml-2 ${isActive ? "text-white" : ""
} ${!icon && "pl-5"}`}
>
{btnText}
</p>
</Link>
{hasChildren && (
<button onClick={handleClick} className="p-2 text-white">
<CaretRight
size={16}
weight="bold"
className={`transition-transform ${isExpanded ? "rotate-90" : ""
}`}
/>
</button>
)}
</div>
{isExpanded && hasChildren && (
<div className="mt-1 rounded-r-lg w-full">
{childOptions.map((childOption, index) => (
<MenuOption
key={index}
{...childOption} // flex and roles go here.
user={user}
isChild={true}
/>
))}
</div>
)}
</div>
);
}

function isVisible({ roles = [], user = null, flex = false }) {
if (!flex && !roles.includes(user?.role)) return false;
if (flex && !!user && !roles.includes(user?.role)) return false;
return true;
}

function generateStorageKey({ key = "" }) {
const _key = key.replace(/\s+/g, "_").toLowerCase();
return `anything_llm_menu_${_key}_expanded`;
}
147 changes: 1 addition & 146 deletions frontend/src/components/SettingsSidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Robot,
Flask,
Gear,
CaretRight,
UserCircleGear,
PencilSimpleLine,
Nut,
Expand All @@ -21,6 +20,7 @@ import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next";
import showToast from "@/utils/toast";
import System from "@/models/system";
import Option from "./MenuOption";

export default function SettingsSidebar() {
const { t } = useTranslation();
Expand Down Expand Up @@ -202,151 +202,6 @@ function SupportEmail() {
);
}

function isVisible({ roles = [], user = null, flex = false }) {
if (!flex && !roles.includes(user?.role)) return false;
if (flex && !!user && !roles.includes(user?.role)) return false;
return true;
}

function generateStorageKey({ key = "" }) {
const _key = key.replace(/\s+/g, "_").toLowerCase();
return `anything_llm_menu_${_key}_expanded`;
}

const Option = ({
btnText,
icon,
href,
childOptions = [],
flex = false,
user = null,
roles = [],
hidden = false,
isChild = false,
}) => {
const storageKey = generateStorageKey({ key: btnText });
const location = window.location.pathname;
const hasChildren = childOptions.length > 0;
const hasVisibleChildren = hasChildren
? childOptions.some((opt) =>
isVisible({ roles: opt.roles, user, flex: opt.flex })
)
: false;
const [isExpanded, setIsExpanded] = useState(() => {
if (hasVisibleChildren) {
const storedValue = localStorage.getItem(storageKey);
if (storedValue !== null) {
return JSON.parse(storedValue);
}
return childOptions.some((child) => child.href === location);
}
return false;
});

useEffect(() => {
if (hasVisibleChildren) {
const shouldExpand = childOptions.some(
(child) => child.href === location
);
if (shouldExpand && !isExpanded) {
setIsExpanded(true);
localStorage.setItem(storageKey, JSON.stringify(true));
}
}
}, [location]);

if (hidden) return null;

// If this option is a parent level option
if (!isChild) {
// and has no children then use its flex props and roles prop directly
if (!hasChildren) {
if (!flex && !roles.includes(user?.role)) return null;
if (flex && !!user && !roles.includes(user?.role)) return null;
}

// if has children and no visible children - remove it.
if (hasChildren && !hasVisibleChildren) return null;
} else {
// is a child so we use it's permissions
if (!flex && !roles.includes(user?.role)) return null;
if (flex && !!user && !roles.includes(user?.role)) return null;
}

const isActive = hasChildren
? (!isExpanded && childOptions.some((child) => child.href === location)) ||
location === href
: location === href;

const handleClick = (e) => {
if (hasChildren) {
e.preventDefault();
const newExpandedState = !isExpanded;
setIsExpanded(newExpandedState);
localStorage.setItem(storageKey, JSON.stringify(newExpandedState));
}
};

return (
<div>
<div
className={`
flex items-center justify-between w-full
transition-all duration-300
rounded-[6px]
${
isActive
? "bg-white/5 font-medium border-outline"
: "hover:bg-white/5"
}
`}
>
<Link
to={href}
className={`flex flex-grow items-center px-[12px] h-[32px] font-medium ${
isChild ? "text-white/70 hover:text-white" : "text-white"
}`}
onClick={hasChildren ? handleClick : undefined}
>
{icon}
<p
className={`${
isChild ? "text-xs" : "text-sm"
} leading-loose whitespace-nowrap overflow-hidden ml-2 ${
isActive ? "text-white" : ""
} ${!icon && "pl-5"}`}
>
{btnText}
</p>
</Link>
{hasChildren && (
<button onClick={handleClick} className="p-2 text-white">
<CaretRight
size={16}
weight="bold"
className={`transition-transform ${
isExpanded ? "rotate-90" : ""
}`}
/>
</button>
)}
</div>
{isExpanded && hasChildren && (
<div className="mt-1 rounded-r-lg w-full">
{childOptions.map((childOption, index) => (
<Option
key={index}
{...childOption} // flex and roles go here.
user={user}
isChild={true}
/>
))}
</div>
)}
</div>
);
};

const SidebarOptions = ({ user = null, t }) => (
<>
<Option
Expand Down

0 comments on commit e0d9221

Please sign in to comment.