gaojintao01
Add files using Git LFS
f8b5d42
import React, { useEffect, useState } from "react";
import { CaretRight } from "@phosphor-icons/react";
import { Link, useLocation } 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 = useLocation();
const hasChildren = childOptions.length > 0;
const hasVisibleChildren = hasVisibleOptions(user, childOptions);
const { isExpanded, setIsExpanded } = useIsExpanded({
storageKey,
hasVisibleChildren,
childOptions,
location: location.pathname,
});
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.pathname)) ||
location.pathname === href
: location.pathname === 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-theme-sidebar-subitem-selected font-medium border-outline"
: "hover:bg-theme-sidebar-subitem-hover"
}
`}
>
<Link
to={href}
className={`flex flex-grow items-center px-[12px] h-[32px] font-medium ${
isChild ? "hover:text-white" : "text-white light:text-black"
}`}
onClick={hasChildren ? handleClick : undefined}
>
{icon}
<p
className={`${
isChild ? "text-xs" : "text-sm"
} leading-loose whitespace-nowrap overflow-hidden ml-2 ${
isActive
? "text-white font-semibold"
: "text-white light:text-black"
} ${!icon && "pl-5"}`}
>
{btnText}
</p>
</Link>
{hasChildren && (
<button onClick={handleClick} className="p-2 text-white">
<CaretRight
size={16}
weight="bold"
// color={isExpanded ? "#000000" : "var(--theme-sidebar-subitem-icon)"}
className={`transition-transform text-white light:text-black ${
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 };
}
/**
* Checks if the child options are visible to the user.
* This hides the top level options if the child options are not visible
* for either the users permissions or the child options hidden prop is set to true by other means.
* If all child options return false for `isVisible` then the parent option will not be visible as well.
* @param {object} user - The user object.
* @param {array} childOptions - The child options.
* @returns {boolean} - True if the child options are visible, false otherwise.
*/
function hasVisibleOptions(user = null, childOptions = []) {
if (!Array.isArray(childOptions) || childOptions?.length === 0) return false;
function isVisible({
roles = [],
user = null,
flex = false,
hidden = false,
}) {
if (hidden) return 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, hidden: opt.hidden })
);
}
function generateStorageKey({ key = "" }) {
const _key = key.replace(/\s+/g, "_").toLowerCase();
return `anything_llm_menu_${_key}_expanded`;
}