You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2023/12/09 11:48:23 UTC

(plc4x) branch feature/new-ui-tool updated: feat: Refactored the UI to have a more responsive layout.

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/new-ui-tool
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/new-ui-tool by this push:
     new 4c51a18aa5 feat: Refactored the UI to have a more responsive layout.
4c51a18aa5 is described below

commit 4c51a18aa539e09c4ff07c51fb431904584edf52
Author: Christofer Dutz <cd...@apache.org>
AuthorDate: Sat Dec 9 12:48:07 2023 +0100

    feat: Refactored the UI to have a more responsive layout.
---
 plc4j/tools/ui/frontend/frontend/package.json      |   7 +-
 plc4j/tools/ui/frontend/frontend/src/App.tsx       | 209 ++-------------------
 .../frontend/frontend/src/components/MainMenu.tsx  |  80 ++++++++
 .../ui/frontend/frontend/src/components/NavBar.tsx |  19 --
 .../frontend/src/components/NavigationTree.tsx     |   2 +-
 .../frontend/frontend/src/layouts/MainLayout.tsx   |  87 +++++++++
 .../ui/frontend/frontend/src/pages/Editor.tsx      |  11 ++
 7 files changed, 200 insertions(+), 215 deletions(-)

diff --git a/plc4j/tools/ui/frontend/frontend/package.json b/plc4j/tools/ui/frontend/frontend/package.json
index bc0ac8b510..728597a923 100644
--- a/plc4j/tools/ui/frontend/frontend/package.json
+++ b/plc4j/tools/ui/frontend/frontend/package.json
@@ -10,12 +10,17 @@
     "preview": "vite preview"
   },
   "dependencies": {
+    "@emotion/react": "^11.11.1",
+    "@emotion/styled": "^11.11.0",
+    "@mui/icons-material": "^5.14.19",
+    "@mui/material": "^5.14.20",
     "axios": "^1.6.2",
     "primeflex": "^3.3.1",
     "primeicons": "^6.0.1",
     "primereact": "^10.2.1",
     "react": "^18.2.0",
-    "react-dom": "^18.2.0"
+    "react-dom": "^18.2.0",
+    "react-router-dom": "^6.20.1"
   },
   "devDependencies": {
     "@types/react": "^18.2.37",
diff --git a/plc4j/tools/ui/frontend/frontend/src/App.tsx b/plc4j/tools/ui/frontend/frontend/src/App.tsx
index 66b59e0432..b645aa9fff 100644
--- a/plc4j/tools/ui/frontend/frontend/src/App.tsx
+++ b/plc4j/tools/ui/frontend/frontend/src/App.tsx
@@ -18,206 +18,27 @@
  */
 
 import './App.css'
-import NavigationTree from "./components/NavigationTree.tsx";
 import axios from 'axios';
-import {useState} from "react";
-import {RestApplicationClient} from "./generated/plc4j-tools-ui-frontend.ts";
-import {TreeItemData} from "./model/TreeItemData.ts";
-import {TabPanel, TabView} from "primereact/tabview";
-import {Menubar} from "primereact/menubar";
-import {MenuItem} from "primereact/menuitem";
-import plc4xLogo from "./assets/plc4x-logo.svg";
-import { Splitter, SplitterPanel } from 'primereact/splitter';
-import {ScrollPanel} from "primereact/scrollpanel";
+import {createBrowserRouter, RouterProvider} from "react-router-dom";
+import MainLayout from "./layouts/MainLayout.tsx";
+import Editor from "./pages/Editor.tsx";
 
 axios.defaults.baseURL = 'http://localhost:8080';
 
-function App() {
-    const [driverTreeRoots, setDriverTreeRoots] = useState<TreeItemData[]>([])
-    const restClient = new RestApplicationClient(axios);
-    const start = <img alt="logo" src={plc4xLogo} height="40" className="mr-8"></img>;
-    const menuModel: MenuItem[] = [
-        /*{
-            label: 'File',
-            icon: 'pi pi-fw pi-file',
-            items: [
-                {
-                    label: 'New',
-                    icon: 'pi pi-fw pi-plus',
-                    items: [
-                        {
-                            label: 'Bookmark',
-                            icon: 'pi pi-fw pi-bookmark'
-                        },
-                        {
-                            label: 'Video',
-                            icon: 'pi pi-fw pi-video'
-                        },
-
-                    ]
-                },
-                {
-                    label: 'Delete',
-                    icon: 'pi pi-fw pi-trash'
-                },
-                {
-                    separator: true
-                },
-                {
-                    label: 'Export',
-                    icon: 'pi pi-fw pi-external-link'
-                }
-            ]
-        },
-        {
-            label: 'Edit',
-            icon: 'pi pi-fw pi-pencil',
-            items: [
-                {
-                    label: 'Left',
-                    icon: 'pi pi-fw pi-align-left'
-                },
-                {
-                    label: 'Right',
-                    icon: 'pi pi-fw pi-align-right'
-                },
-                {
-                    label: 'Center',
-                    icon: 'pi pi-fw pi-align-center'
-                },
-                {
-                    label: 'Justify',
-                    icon: 'pi pi-fw pi-align-justify'
-                },
-
-            ]
-        },
-        {
-            label: 'Users',
-            icon: 'pi pi-fw pi-user',
-            items: [
-                {
-                    label: 'New',
-                    icon: 'pi pi-fw pi-user-plus',
-
-                },
-                {
-                    label: 'Delete',
-                    icon: 'pi pi-fw pi-user-minus',
-
-                },
-                {
-                    label: 'Search',
-                    icon: 'pi pi-fw pi-users',
-                    items: [
-                        {
-                            label: 'Filter',
-                            icon: 'pi pi-fw pi-filter',
-                            items: [
-                                {
-                                    label: 'Print',
-                                    icon: 'pi pi-fw pi-print'
-                                }
-                            ]
-                        },
-                        {
-                            icon: 'pi pi-fw pi-bars',
-                            label: 'List'
-                        }
-                    ]
-                }
-            ]
-        },
-        {
-            label: 'Events',
-            icon: 'pi pi-fw pi-calendar',
-            items: [
-                {
-                    label: 'Edit',
-                    icon: 'pi pi-fw pi-pencil',
-                    items: [
-                        {
-                            label: 'Save',
-                            icon: 'pi pi-fw pi-calendar-plus'
-                        },
-                        {
-                            label: 'Delete',
-                            icon: 'pi pi-fw pi-calendar-minus'
-                        }
-                    ]
-                },
-                {
-                    label: 'Archive',
-                    icon: 'pi pi-fw pi-calendar-times',
-                    items: [
-                        {
-                            label: 'Remove',
-                            icon: 'pi pi-fw pi-calendar-minus'
-                        }
-                    ]
-                }
-            ]
-        },*/
-        {
-            label: 'Dummy',
-            icon: 'pi pi-fw pi-file',
-        }
-    ];
-
-    function updateDriverList() {
-        const driverList = restClient.getDriverList();
-        driverList.then(response => {
-            let newDriverTreeRoots: TreeItemData[] = [];
-            response.data.map(driverValue => {
-                newDriverTreeRoots = [...newDriverTreeRoots, {
-                    id: driverValue.code,
-                    name: driverValue.name,
-                    type: "DRIVER",
-                    supportsDiscovery: driverValue.supportsDiscovery,
-                    supportsBrowsing: false,
-                    supportsReading: false,
-                    supportsWriting: false,
-                    supportsSubscribing: false,
-                    supportsPublishing: false,
-                }]
-            })
-            setDriverTreeRoots(newDriverTreeRoots)
-        })
-    }
-    if(driverTreeRoots.length == 0) {
-        updateDriverList()
-    }
+// We're actually just using this concept in order to separate the layout for the general page from the content.
+const router = createBrowserRouter([
+    {
+        path: '/',
+        element: <MainLayout/>,
+        children: [
+            {path: '/', element: <Editor/>},
+        ]
+    },
+])
 
+function App() {
     return (
-        <div className="flex flex-column w-screen h-screen">
-            <header>
-                <Menubar start={start} model={menuModel}/>
-            </header>
-            <Splitter className="flex h-full">
-                <SplitterPanel className="flex">
-                    <TabView style={{width: '100%', height:'100%'}}>
-                        <TabPanel header="By Driver" className="m-0">
-                            <ScrollPanel style={{width: '100%', height:'100%'}} className="h-full">
-                                <NavigationTree treeItems={driverTreeRoots}/>
-                            </ScrollPanel>
-                        </TabPanel>
-                        <TabPanel header="By Device">
-                            <ScrollPanel style={{width: '100%', height:'100%'}}>
-                                <NavigationTree treeItems={driverTreeRoots}/>
-                            </ScrollPanel>
-                        </TabPanel>
-                    </TabView>
-                </SplitterPanel>
-                <SplitterPanel className="w-full">
-                    <TabView>
-
-                        <TabPanel key={"ads://192.168.23.20"} header="ads://192.168.23.20" closable={true}>
-                            <p>test</p>
-                        </TabPanel>
-                    </TabView>
-                </SplitterPanel>
-            </Splitter>
-        </div>
+        <RouterProvider router={router}/>
     )
 }
 
diff --git a/plc4j/tools/ui/frontend/frontend/src/components/MainMenu.tsx b/plc4j/tools/ui/frontend/frontend/src/components/MainMenu.tsx
new file mode 100644
index 0000000000..dd7a06cd06
--- /dev/null
+++ b/plc4j/tools/ui/frontend/frontend/src/components/MainMenu.tsx
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import plc4xLogo from "../assets/plc4x-logo.svg";
+import {Divider, Toolbar} from "@mui/material";
+import {Image} from "primereact/image";
+import {TabPanel, TabView} from "primereact/tabview";
+import {ScrollPanel} from "primereact/scrollpanel";
+import NavigationTree from "./NavigationTree.tsx";
+import {useState} from "react";
+import {TreeItemData} from "../model/TreeItemData.ts";
+import {RestApplicationClient} from "../generated/plc4j-tools-ui-frontend.ts";
+import axios from "axios";
+
+export default function MainMenu() {
+    const [driverTreeRoots, setDriverTreeRoots] = useState<TreeItemData[]>([])
+    const restClient = new RestApplicationClient(axios);
+
+    function updateDriverList() {
+        const driverList = restClient.getDriverList();
+        driverList.then(response => {
+            let newDriverTreeRoots: TreeItemData[] = [];
+            response.data.map(driverValue => {
+                newDriverTreeRoots = [...newDriverTreeRoots, {
+                    id: driverValue.code,
+                    name: driverValue.name,
+                    type: "DRIVER",
+                    supportsDiscovery: driverValue.supportsDiscovery,
+                    supportsBrowsing: false,
+                    supportsReading: false,
+                    supportsWriting: false,
+                    supportsSubscribing: false,
+                    supportsPublishing: false,
+                }]
+            })
+            setDriverTreeRoots(newDriverTreeRoots)
+        })
+    }
+
+    if(driverTreeRoots.length == 0) {
+        updateDriverList()
+    }
+
+    return (
+        <div>
+            <Toolbar>
+                <Image src={plc4xLogo} width="200px"/>
+            </Toolbar>
+            <Divider/>
+            <TabView style={{width: '100%', height:'100%'}}>
+                <TabPanel header="By Driver" className="m-0">
+                    <ScrollPanel style={{width: '100%', height:'100%'}} className="h-full">
+                        <NavigationTree treeItems={driverTreeRoots}/>
+                    </ScrollPanel>
+                </TabPanel>
+                <TabPanel header="By Device">
+                    <ScrollPanel style={{width: '100%', height:'100%'}}>
+                        <NavigationTree  treeItems={driverTreeRoots}/>
+                    </ScrollPanel>
+                </TabPanel>
+            </TabView>
+        </div>
+    )
+}
\ No newline at end of file
diff --git a/plc4j/tools/ui/frontend/frontend/src/components/NavBar.tsx b/plc4j/tools/ui/frontend/frontend/src/components/NavBar.tsx
deleted file mode 100644
index bd244d07ab..0000000000
--- a/plc4j/tools/ui/frontend/frontend/src/components/NavBar.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
diff --git a/plc4j/tools/ui/frontend/frontend/src/components/NavigationTree.tsx b/plc4j/tools/ui/frontend/frontend/src/components/NavigationTree.tsx
index 56741eec57..e6d4fad617 100644
--- a/plc4j/tools/ui/frontend/frontend/src/components/NavigationTree.tsx
+++ b/plc4j/tools/ui/frontend/frontend/src/components/NavigationTree.tsx
@@ -54,6 +54,6 @@ export default function NavigationTree({treeItems}: NavigationTreeProps) {
         }
     }
 
-    let treeNodes: TreeNode[] = treeItems.map(value => createTreeNode(value))
+    const treeNodes: TreeNode[] = treeItems.map(value => createTreeNode(value))
     return <Tree value={treeNodes} selectionMode="single"/>
 }
\ No newline at end of file
diff --git a/plc4j/tools/ui/frontend/frontend/src/layouts/MainLayout.tsx b/plc4j/tools/ui/frontend/frontend/src/layouts/MainLayout.tsx
new file mode 100644
index 0000000000..91221e5674
--- /dev/null
+++ b/plc4j/tools/ui/frontend/frontend/src/layouts/MainLayout.tsx
@@ -0,0 +1,87 @@
+import {Outlet} from "react-router-dom";
+import {AppBar, Box, CssBaseline, Drawer, IconButton, Toolbar, Typography} from "@mui/material";
+import MenuIcon from "@mui/icons-material/Menu";
+import MainMenu from "../components/MainMenu.tsx";
+import React from "react";
+
+const drawerWidth = 480;
+
+export default function MainLayout() {
+    const [mobileOpen, setMobileOpen] = React.useState(false);
+
+    const handleDrawerToggle = () => {
+        setMobileOpen(!mobileOpen);
+    };
+
+    return(
+        <Box sx={{display: 'flex'}}>
+            <CssBaseline/>
+            {/* This is the main application bar */}
+            <AppBar
+                position="fixed"
+                sx={{
+                    width: {sm: `calc(100% - ${drawerWidth}px)`},
+                    ml: {sm: `${drawerWidth}px`},
+                }}>
+                <Toolbar>
+                    {/* This is the menu button, which is displayed in very narrow screens (Hidden otherwise) */}
+                    <IconButton
+                        color="inherit"
+                        aria-label="open drawer"
+                        edge="start"
+                        onClick={handleDrawerToggle}
+                        sx={{mr: 2, display: {sm: 'none'}}}
+                    >
+                        <MenuIcon/>
+                    </IconButton>
+                    {/* This is the title text in the main application bar */}
+                    <Typography variant="h6" noWrap component="div">
+                        Responsive drawer
+                    </Typography>
+                </Toolbar>
+            </AppBar>
+            {/* This is the drawer content (Menu) */}
+            <Box
+                component="nav"
+                sx={{width: {sm: drawerWidth}, flexShrink: {sm: 0}}}
+                aria-label="mailbox folders"
+            >
+                {/* This is the dynamic drawer used on mobile devices. */}
+                <Drawer
+                    variant="temporary"
+                    open={mobileOpen}
+                    onClose={handleDrawerToggle}
+                    ModalProps={{
+                        keepMounted: true, // Better open performance on mobile.
+                    }}
+                    sx={{
+                        display: {xs: 'block', sm: 'none'},
+                        '& .MuiDrawer-paper': {boxSizing: 'border-box', width: drawerWidth},
+                    }}
+                >
+                    <MainMenu/>
+                </Drawer>
+                {/* This is the static drawer used on normal devices. */}
+                <Drawer
+                    variant="permanent"
+                    sx={{
+                        display: {xs: 'none', sm: 'block'},
+                        '& .MuiDrawer-paper': {boxSizing: 'border-box', width: drawerWidth},
+                    }}
+                    open
+                >
+                    <MainMenu/>
+                </Drawer>
+            </Box>
+            {/* This is the main application content */}
+            <Box
+                component="main"
+                sx={{flexGrow: 1, p: 3, width: {sm: `calc(100% - ${drawerWidth}px)`}}}
+            >
+                {/* This is a dummy toolbar to prevent the content from going behind the real application bar */}
+                <Toolbar/>
+                <Outlet/>
+            </Box>
+        </Box>
+    )
+}
\ No newline at end of file
diff --git a/plc4j/tools/ui/frontend/frontend/src/pages/Editor.tsx b/plc4j/tools/ui/frontend/frontend/src/pages/Editor.tsx
new file mode 100644
index 0000000000..0e9f4a20cf
--- /dev/null
+++ b/plc4j/tools/ui/frontend/frontend/src/pages/Editor.tsx
@@ -0,0 +1,11 @@
+import {TabPanel, TabView} from "primereact/tabview";
+
+export default function Editor() {
+    return (
+        <TabView style={{width: "100%", height: "100%"}}>
+            <TabPanel key={"ads://192.168.23.20"} header="ads://192.168.23.20" closable={true}>
+                <p>test</p>
+            </TabPanel>
+        </TabView>
+    )
+}