θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
168 changes: 168 additions & 0 deletions frontend/src/components/SettingsSidebar/MenuOption/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
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 = hasVisibleOptions(user, childOptions);
const { isExpanded, setIsExpanded } = useIsExpanded({
storageKey,
hasVisibleChildren,
childOptions,
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 useIsExpanded({
storageKey = "",
hasVisibleChildren = false,
childOptions = [],
location = null,
}) {
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]);

return { isExpanded, setIsExpanded };
}

function hasVisibleOptions(user = null, childOptions = []) {
if (!Array.isArray(childOptions) || childOptions?.length === 0) return false;

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;
}

return childOptions.some((opt) =>
isVisible({ roles: opt.roles, user, flex: opt.flex })
);
}

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