mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Make web pages pluggable
Refactor the page handling of the web interface to allow plugins to add their own pages.
This commit is contained in:
1 parent
73c713462c
commit
3d26371fa9
3 files changed
+67
-41
No files matched your search
@@ -439,6 +439,21 @@ class BaseControlPlugin {
|
||||
* form.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plugin supplied pages
|
||||
* @typedef {Object} module:Lib/plugin~Page
|
||||
* @property {string} path - URL path to this page.
|
||||
* @property {string=} sidebarPath -
|
||||
* If present and this path matches one of the pages in the sidebar it
|
||||
* will cause that sidebar entry to be highlighted as active.
|
||||
* @property {string=} sidebarName -
|
||||
* If present creates an entry in the sidebar for this page with the
|
||||
* given text.
|
||||
* @property {ReactNode} content -
|
||||
* A react node which is rendered when this page is navigated to.
|
||||
* Should render a PageLayout.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for web interface plugins
|
||||
*
|
||||
@@ -477,6 +492,13 @@ class BaseWebPlugin {
|
||||
* @type {Array<module:lib/plugin~LoginForm>}
|
||||
*/
|
||||
this.loginForms = [];
|
||||
|
||||
/**
|
||||
* List of pages provided by this plugin
|
||||
*
|
||||
* @type {Array<module:lib/plugin~Page>}
|
||||
*/
|
||||
this.pages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { Switch, Route, useHistory } from "react-router-dom";
|
||||
import { Dropdown, Layout, Menu } from "antd";
|
||||
import UserOutlined from "@ant-design/icons/UserOutlined";
|
||||
@@ -7,46 +7,21 @@ import webUiPackage from "../../package.json";
|
||||
import { useAccount } from "../model/account";
|
||||
import ErrorBoundary from "./ErrorBoundary";
|
||||
import ErrorPage from "./ErrorPage";
|
||||
import PluginsContext from "./PluginsContext";
|
||||
import { pages } from "../pages";
|
||||
|
||||
const { Header, Sider } = Layout;
|
||||
|
||||
|
||||
import MasterPage from "./MasterPage";
|
||||
import SlavesPage from "./SlavesPage";
|
||||
import InstancesPage from "./InstancesPage";
|
||||
import InstanceViewPage from "./InstanceViewPage";
|
||||
import UsersPage from "./UsersPage";
|
||||
import UserViewPage from "./UserViewPage";
|
||||
import RolesPage from "./RolesPage";
|
||||
import RoleViewPage from "./RoleViewPage";
|
||||
import PluginsPage from "./PluginsPage";
|
||||
import PluginViewPage from "./PluginViewPage";
|
||||
|
||||
const pages = [
|
||||
{ path: "/instances/:id/view", sidebarPath: "/instances", content: <InstanceViewPage /> },
|
||||
{ path: "/users/:name/view", sidebarPath: "/users", content: <UserViewPage /> },
|
||||
{ path: "/roles/:id/view", sidebarPath: "/roles", content: <RoleViewPage /> },
|
||||
{ path: "/plugins/:name/view", sidebarPath: "/plugins", content: <PluginViewPage /> },
|
||||
];
|
||||
|
||||
const sidebar = [
|
||||
{ name: "Master", path: "/master", content: <MasterPage /> },
|
||||
{ name: "Slaves", path: "/slaves", content: <SlavesPage /> },
|
||||
{ name: "Instances", path: "/instances", content: <InstancesPage />},
|
||||
{ name: "Users", path: "/users", content: <UsersPage /> },
|
||||
{ name: "Roles", path: "/roles", content: <RolesPage /> },
|
||||
{ name: "Plugins", path: "/plugins", content: <PluginsPage /> },
|
||||
];
|
||||
|
||||
|
||||
export default function SiteLayout(props) {
|
||||
let history = useHistory();
|
||||
let [sidebarPath, setSidebarPath] = useState(null);
|
||||
let [currentSidebarPath, setCurrentSidebarPath] = useState(null);
|
||||
let account = useAccount();
|
||||
let plugins = useContext(PluginsContext);
|
||||
|
||||
function SetSidebar(setSidebarProps) {
|
||||
useEffect(() => {
|
||||
setSidebarPath(setSidebarProps.path);
|
||||
setCurrentSidebarPath(setSidebarProps.path);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
@@ -57,6 +32,10 @@ export default function SiteLayout(props) {
|
||||
<Menu.Item danger onClick={() => { account.logOut(); }}>Log out</Menu.Item>
|
||||
</Menu>;
|
||||
|
||||
let combinedPages = [...pages];
|
||||
for (let plugin of plugins.values()) {
|
||||
combinedPages.push(...plugin.pages);
|
||||
}
|
||||
|
||||
return <Layout style={{ minHeight: "100vh" }}>
|
||||
<Header className="header">
|
||||
@@ -86,27 +65,23 @@ export default function SiteLayout(props) {
|
||||
>
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectedKeys={[sidebarPath]}
|
||||
selectedKeys={[currentSidebarPath]}
|
||||
style={{ height: "100%", borderRight: 0 }}
|
||||
onClick={({ key }) => history.push(key)}
|
||||
>
|
||||
{sidebar.map(({name, path}) => <Menu.Item key={path}>{name}</Menu.Item>)}
|
||||
{combinedPages.map(({sidebarName, path}) => (
|
||||
sidebarName ? <Menu.Item key={path}>{sidebarName}</Menu.Item> : null)
|
||||
)}
|
||||
</Menu>
|
||||
</Sider>
|
||||
<Layout className="site-layout-content-container">
|
||||
<Switch>
|
||||
{sidebar.map(({path, content}) => <Route exact path={path} key={path}>
|
||||
<SetSidebar path={path} />
|
||||
{combinedPages.map(({path, sidebarPath, content}) => <Route exact path={path} key={path}>
|
||||
<SetSidebar path={sidebarPath ? sidebarPath : path} />
|
||||
<ErrorBoundary Component={ErrorPage}>
|
||||
{content}
|
||||
</ErrorBoundary>
|
||||
</Route>)}
|
||||
{pages.map(page => <Route exact path={page.path} key={page.path}>
|
||||
<SetSidebar path={page.sidebarPath} />
|
||||
<ErrorBoundary Component={ErrorPage}>
|
||||
{page.content}
|
||||
</ErrorBoundary>
|
||||
</Route>)}
|
||||
<Route>
|
||||
<SetSidebar path={null} />
|
||||
</Route>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
|
||||
import MasterPage from "./components/MasterPage";
|
||||
import SlavesPage from "./components/SlavesPage";
|
||||
import InstancesPage from "./components/InstancesPage";
|
||||
import InstanceViewPage from "./components/InstanceViewPage";
|
||||
import UsersPage from "./components/UsersPage";
|
||||
import UserViewPage from "./components/UserViewPage";
|
||||
import RolesPage from "./components/RolesPage";
|
||||
import RoleViewPage from "./components/RoleViewPage";
|
||||
import PluginsPage from "./components/PluginsPage";
|
||||
import PluginViewPage from "./components/PluginViewPage";
|
||||
|
||||
|
||||
export const pages = [
|
||||
{ path: "/master", sidebarName: "Master", content: <MasterPage /> },
|
||||
{ path: "/slaves", sidebarName: "Slaves", content: <SlavesPage /> },
|
||||
{ path: "/instances", sidebarName: "Instances", content: <InstancesPage />},
|
||||
{ path: "/instances/:id/view", sidebarPath: "/instances", content: <InstanceViewPage /> },
|
||||
{ path: "/users", sidebarName: "Users", content: <UsersPage /> },
|
||||
{ path: "/users/:name/view", sidebarPath: "/users", content: <UserViewPage /> },
|
||||
{ path: "/roles", sidebarName: "Roles", content: <RolesPage /> },
|
||||
{ path: "/roles/:id/view", sidebarPath: "/roles", content: <RoleViewPage /> },
|
||||
{ path: "/plugins", sidebarName: "Plugins", content: <PluginsPage /> },
|
||||
{ path: "/plugins/:name/view", sidebarPath: "/plugins", content: <PluginViewPage /> },
|
||||
];
|
||||
|
||||
export const sidebarPages = [
|
||||
];
|
||||
Reference in new issue
Block a user