import React, { Fragment, useContext, useEffect, useState } from "react"; import { Route, Routes, useNavigate } from "react-router-dom"; import { Dropdown, Layout, Menu, MenuProps } from "antd"; import UserOutlined from "@ant-design/icons/UserOutlined"; import webUiPackage from "../../package.json"; import { useAccount } from "../model/account"; import ErrorBoundary from "./ErrorBoundary"; import ErrorPage from "./ErrorPage"; import ControlContext from "./ControlContext"; import { pages } from "../pages"; import { DraggingContext } from "../model/is_dragging"; type MenuItem = Required["items"][number]; const { Header, Sider } = Layout; function isActiveDropzone(element: HTMLElement | null): boolean { if (!element) { return false; } const checkDepth = 5; let depth = 0; while (element && depth < checkDepth) { if (element.classList.contains("dropzone")) { return element.classList.contains("enabled"); } element = element.parentElement; depth += 1; } return false; } export default function SiteLayout() { let navigate = useNavigate(); let [currentSidebarPath, setCurrentSidebarPath] = useState(null); let account = useAccount(); let plugins = useContext(ControlContext).plugins; const [dragging, setDragging] = useState(0); function SetSidebar(props: { path: string | null }) { useEffect(() => { setCurrentSidebarPath(props.path); }); return null; } let accountMenuProps: MenuProps = { onClick: ({ key }: { key: string }) => { if (key === "user") { navigate(`/users/${account.name}/view`); } else if (key === "logOut") { account.logOut(); } }, items: [ { label: account.name, key: "user" }, { type: "divider" }, { label: "Log out", danger: true, key: "logOut" }, ], }; let combinedPages = [...pages]; for (let plugin of plugins.values()) { combinedPages.push(...plugin.pages); } let menuItems: MenuItem[] = []; let menuGroups = new Map(); for (let { sidebarName, sidebarGroup, permission, path } of combinedPages) { if ( !sidebarName || permission && ( typeof permission === "function" ? !permission(account) : !account.hasPermission(permission) ) ) { continue; } if (sidebarGroup) { let group = menuGroups.get(sidebarGroup); if (!group) { group = []; menuGroups.set(sidebarGroup, group); } group.push({ label: sidebarName, key: path }); } else { menuItems.push({ label: sidebarName, key: path }); } } for (let [name, group] of menuGroups) { menuItems.push({ type: "group", label: name, children: group, key: name }); } // Prevent issues with event ordering let dragChange = 0; function setDraggingProxy(value: number) { dragChange += value; setDragging(dragging + dragChange); } return setDraggingProxy(1)} onDragLeave={() => setDraggingProxy(-1)} // Reset dragging state when dropped onDrop={(e) => { setDragging(0); // Prevent dropping outside of dropzone if (!isActiveDropzone(e.target as HTMLElement)) { e.preventDefault(); e.stopPropagation(); } }} // Allow entire document to be a drag target onDragOver={(e) => { e.preventDefault(); // Change cursor if (isActiveDropzone(e.target as HTMLElement)) { e.dataTransfer.dropEffect = "copy"; } else { e.dataTransfer.dropEffect = "none"; } }} > 0}>
Clusterio {webUiPackage.version}
navigate(key)} items={menuItems} /> {combinedPages.map(({ path, sidebarPath, content }) => {content} } />)} } /> ; }