/*
GoToSocial
Copyright (C) GoToSocial Authors admin@gotosocial.org
SPDX-License-Identifier: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import React, { PropsWithChildren } from "react";
import { Link, useRoute } from "wouter";
import {
BaseUrlContext,
MenuLevelContext,
useBaseUrl,
useHasPermission,
useMenuLevel,
} from "./util";
import UserLogoutCard from "../../components/user-logout-card";
import { nanoid } from "nanoid";
export interface MenuItemProps {
/**
* Name / title of this menu item.
*/
name?: string;
/**
* Url path component for this menu item.
*/
itemUrl: string;
/**
* If this menu item is a category containing
* children, which child should be selected by
* default when category title is clicked.
*
* Optional, use for categories only.
*/
defaultChild?: string;
/**
* Permissions required to access this
* menu item (none, "moderator", "admin").
*/
permissions?: string[];
/**
* Fork-awesome string to render
* icon for this menu item.
*/
icon?: string;
}
export function MenuItem(props: PropsWithChildren) {
const {
name,
itemUrl,
defaultChild,
permissions,
icon,
children,
} = props;
// Derive where this item is
// in terms of URL routing.
const baseUrl = useBaseUrl();
const thisUrl = [ baseUrl, itemUrl ].join('/');
// Derive where this item is in
// terms of nesting within the menu.
const thisLevel = useMenuLevel();
const nextLevel = thisLevel+1;
const topLevel = thisLevel === 0;
// Check whether this item is currently active
// (ie., user has selected it in the menu).
//
// This uses a wildcard to mark both parent
// and relevant child as active.
//
// See:
// https://github.com/molefrog/wouter?tab=readme-ov-file#useroute-route-matching-and-parameters
const [isActive] = useRoute([ thisUrl, "*?" ].join("/"));
// Don't render item if logged-in user
// doesn't have permissions to use it.
if (!useHasPermission(permissions)) {
return null;
}
// Check whether this item has children.
const hasChildren = children !== undefined;
const childrenArray = hasChildren && Array.isArray(children);
// Class name of the item varies depending
// on where it is in the menu, and whether
// it has children beneath it or not.
const classNames: string[] = [];
if (topLevel) {
classNames.push("category", "top-level");
} else {
switch (true) {
case thisLevel === 1 && hasChildren:
classNames.push("category", "expanding");
break;
case thisLevel === 1 && !hasChildren:
classNames.push("view", "expanding");
break;
case thisLevel >= 2 && hasChildren:
classNames.push("nested", "category");
break;
case thisLevel >= 2 && !hasChildren:
classNames.push("nested", "view");
break;
}
}
if (isActive) {
classNames.push("active");
}
let content: React.JSX.Element | null;
if ((isActive || topLevel) && childrenArray) {
// Render children as a nested list.
content =
{children}
;
} else if (isActive && hasChildren) {
// Render child as solo element.
content = <>{children}>;
} else {
// Not active: hide children.
content = null;
}
// If a default child is defined, this item should point to that.
const href = defaultChild ? [ thisUrl, defaultChild ].join("/") : thisUrl;
return (
{icon && }
{name}
{ content &&
{content}
}
);
}
export interface SidebarMenuProps{}
export function SidebarMenu({ children }: PropsWithChildren) {
return (