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 (
{icon}

{btnText}

{hasChildren && ( )}
{isExpanded && hasChildren && (
{childOptions.map((childOption, index) => ( ))}
)}
); } 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`; }