Add mobile compatibility to management UI
This commit is contained in:
parent
9603f59b96
commit
3a36b862df
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import React, { Component } from "react"
|
import React, { Component } from "react"
|
||||||
import { Route, Switch, Link } from "react-router-dom"
|
import { Route, Switch, Link, withRouter } from "react-router-dom"
|
||||||
import api from "../../api"
|
import api from "../../api"
|
||||||
import { ReactComponent as Plus } from "../../res/plus.svg"
|
import { ReactComponent as Plus } from "../../res/plus.svg"
|
||||||
import Instance from "./Instance"
|
import Instance from "./Instance"
|
||||||
@ -28,10 +28,17 @@ class Dashboard extends Component {
|
|||||||
instances: {},
|
instances: {},
|
||||||
clients: {},
|
clients: {},
|
||||||
plugins: {},
|
plugins: {},
|
||||||
|
sidebarOpen: false,
|
||||||
}
|
}
|
||||||
window.maubot = this
|
window.maubot = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (this.props.location !== prevProps.location) {
|
||||||
|
this.setState({ sidebarOpen: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async componentWillMount() {
|
async componentWillMount() {
|
||||||
const [instanceList, clientList, pluginList] = await Promise.all([
|
const [instanceList, clientList, pluginList] = await Promise.all([
|
||||||
api.getInstances(), api.getClients(), api.getPlugins()])
|
api.getInstances(), api.getClients(), api.getPlugins()])
|
||||||
@ -90,15 +97,15 @@ class Dashboard extends Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div className="dashboard">
|
return <div className={`dashboard ${this.state.sidebarOpen ? "sidebar-open" : ""}`}>
|
||||||
<Link to="/" className="title">
|
<Link to="/" className="title">
|
||||||
<img src="/favicon.png" alt=""/>
|
<img src="/favicon.png" alt=""/>
|
||||||
Maubot Manager
|
Maubot Manager
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="user">
|
<div className="user">
|
||||||
<span>{localStorage.username}</span>
|
<span>{localStorage.username}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="sidebar">
|
<nav className="sidebar">
|
||||||
<div className="instances list">
|
<div className="instances list">
|
||||||
<div className="title">
|
<div className="title">
|
||||||
@ -122,6 +129,14 @@ class Dashboard extends Component {
|
|||||||
{this.renderList("plugins", Plugin.ListEntry)}
|
{this.renderList("plugins", Plugin.ListEntry)}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<div className="topbar">
|
||||||
|
<div className={`hamburger ${this.state.sidebarOpen ? "active" : ""}`}
|
||||||
|
onClick={evt => this.setState({ sidebarOpen: !this.state.sidebarOpen })}>
|
||||||
|
<span/><span/><span/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<main className="view">
|
<main className="view">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/" exact render={() => "Hello, World!"}/>
|
<Route path="/" exact render={() => "Hello, World!"}/>
|
||||||
@ -145,4 +160,4 @@ class Dashboard extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dashboard
|
export default withRouter(Dashboard)
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
|
|
||||||
> .entry
|
> .entry
|
||||||
display: block
|
display: block
|
||||||
|
|
||||||
|
@media screen and (max-width: 55rem)
|
||||||
|
width: calc(100% - 1rem)
|
||||||
width: calc(50% - 1rem)
|
width: calc(50% - 1rem)
|
||||||
margin: .5rem
|
margin: .5rem
|
||||||
|
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
height: 8rem
|
height: 8rem
|
||||||
border-radius: 50%
|
border-radius: 50%
|
||||||
|
|
||||||
|
@media screen and (max-width: 40rem)
|
||||||
|
margin: 0 auto 1rem
|
||||||
|
|
||||||
> img.avatar
|
> img.avatar
|
||||||
position: absolute
|
position: absolute
|
||||||
display: block
|
display: block
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
> div.client
|
> div.client
|
||||||
display: flex
|
display: flex
|
||||||
margin: 2rem 4rem
|
|
||||||
|
|
||||||
> div.sidebar
|
> div.sidebar
|
||||||
vertical-align: top
|
vertical-align: top
|
||||||
@ -36,3 +35,10 @@
|
|||||||
|
|
||||||
> div.instances
|
> div.instances
|
||||||
+instancelist
|
+instancelist
|
||||||
|
|
||||||
|
@media screen and (max-width: 40rem)
|
||||||
|
flex-wrap: wrap
|
||||||
|
|
||||||
|
> div.sidebar, > div.info
|
||||||
|
width: 100%
|
||||||
|
margin-right: 0
|
||||||
|
@ -5,3 +5,22 @@
|
|||||||
[row3-start] "sidebar main" auto [row3-end]
|
[row3-start] "sidebar main" auto [row3-end]
|
||||||
/ 15rem auto;
|
/ 15rem auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 35rem) {
|
||||||
|
.dashboard {
|
||||||
|
grid-template:
|
||||||
|
[row1-start] "topbar" 3.5rem [row1-end]
|
||||||
|
[row2-start] "main" auto [row2-end]
|
||||||
|
/ auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard.sidebar-open {
|
||||||
|
grid-template:
|
||||||
|
[row1-start] "title topbar" 3.5rem [row1-end]
|
||||||
|
[row2-start] "user main" 2.5rem [row2-end]
|
||||||
|
[row3-start] "sidebar main" auto [row3-end]
|
||||||
|
/ 15rem 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -60,11 +60,19 @@
|
|||||||
border-radius: .25rem
|
border-radius: .25rem
|
||||||
|
|
||||||
@import sidebar
|
@import sidebar
|
||||||
|
@import topbar
|
||||||
|
|
||||||
|
@media screen and (max-width: 35rem)
|
||||||
|
&:not(.sidebar-open)
|
||||||
|
> nav.sidebar, > a.title, > div.user
|
||||||
|
display: none !important
|
||||||
|
|
||||||
> main.view
|
> main.view
|
||||||
grid-area: main
|
grid-area: main
|
||||||
border-left: 1px solid $border-color
|
border-left: 1px solid $border-color
|
||||||
|
|
||||||
|
overflow-y: scroll
|
||||||
|
|
||||||
@import client/index
|
@import client/index
|
||||||
@import instance
|
@import instance
|
||||||
@import plugin
|
@import plugin
|
||||||
@ -74,6 +82,12 @@
|
|||||||
margin-top: 5rem
|
margin-top: 5rem
|
||||||
font-size: 1.5rem
|
font-size: 1.5rem
|
||||||
|
|
||||||
|
> div:not(.not-found)
|
||||||
|
margin: 2rem 4rem
|
||||||
|
|
||||||
|
@media screen and (max-width: 50rem)
|
||||||
|
margin: 2rem 1rem
|
||||||
|
|
||||||
div.buttons
|
div.buttons
|
||||||
+button-group
|
+button-group
|
||||||
display: flex
|
display: flex
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
> div.instance
|
> div.instance
|
||||||
margin: 2rem 4rem
|
|
||||||
|
|
||||||
> div.preference-table
|
> div.preference-table
|
||||||
.select-client
|
.select-client
|
||||||
display: flex
|
display: flex
|
||||||
|
@ -28,6 +28,10 @@
|
|||||||
border-radius: .25rem
|
border-radius: .25rem
|
||||||
margin-top: 3rem
|
margin-top: 3rem
|
||||||
|
|
||||||
|
@media screen and (max-width: 27rem)
|
||||||
|
margin: 3rem 1rem 0
|
||||||
|
width: calc(100% - 2rem)
|
||||||
|
|
||||||
h1
|
h1
|
||||||
color: $primary
|
color: $primary
|
||||||
margin: 3rem 0
|
margin: 3rem 0
|
||||||
@ -35,13 +39,14 @@
|
|||||||
input, button
|
input, button
|
||||||
margin: .5rem 2.5rem
|
margin: .5rem 2.5rem
|
||||||
height: 3rem
|
height: 3rem
|
||||||
width: 20rem
|
width: calc(100% - 5rem)
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
input
|
input
|
||||||
+input
|
+input
|
||||||
|
|
||||||
button
|
button
|
||||||
+button($width: 20rem, $height: 3rem, $padding: 0)
|
+button($width: calc(100% - 5rem), $height: 3rem, $padding: 0)
|
||||||
+main-color-button
|
+main-color-button
|
||||||
|
|
||||||
.spinner
|
.spinner
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
> .plugin
|
> .plugin
|
||||||
margin: 2rem 4rem
|
|
||||||
|
|
||||||
> .upload-box
|
> .upload-box
|
||||||
+upload-box
|
+upload-box
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
> .sidebar
|
> nav.sidebar
|
||||||
grid-area: sidebar
|
grid-area: sidebar
|
||||||
background-color: white
|
background-color: white
|
||||||
|
|
||||||
|
74
maubot/management/frontend/src/style/pages/topbar.sass
Normal file
74
maubot/management/frontend/src/style/pages/topbar.sass
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// maubot - A plugin-based Matrix bot system.
|
||||||
|
// Copyright (C) 2018 Tulir Asokan
|
||||||
|
//
|
||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
.topbar
|
||||||
|
background-color: $primary
|
||||||
|
|
||||||
|
display: flex
|
||||||
|
justify-items: center
|
||||||
|
align-items: center
|
||||||
|
padding: 0 .75rem
|
||||||
|
|
||||||
|
@media screen and (min-width: calc(35rem + 1px))
|
||||||
|
display: none
|
||||||
|
|
||||||
|
// Hamburger menu based on "Pure CSS Hamburger fold-out menu" codepen by Erik Terwan (MIT license)
|
||||||
|
// https://codepen.io/erikterwan/pen/EVzeRP
|
||||||
|
|
||||||
|
.hamburger
|
||||||
|
display: block
|
||||||
|
user-select: none
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
> span
|
||||||
|
display: block
|
||||||
|
width: 29px
|
||||||
|
height: 4px
|
||||||
|
margin-bottom: 5px
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
background: white
|
||||||
|
border-radius: 3px
|
||||||
|
|
||||||
|
z-index: 1
|
||||||
|
|
||||||
|
transform-origin: 4px 0
|
||||||
|
|
||||||
|
//transition: transform 0.5s cubic-bezier(0.77, 0.2, 0.05, 1.0), background 0.5s cubic-bezier(0.77, 0.2, 0.05, 1.0), opacity 0.55s ease
|
||||||
|
|
||||||
|
&:nth-of-type(1)
|
||||||
|
transform-origin: 0 0
|
||||||
|
|
||||||
|
&:nth-of-type(3)
|
||||||
|
transform-origin: 0 100%
|
||||||
|
|
||||||
|
transform: translateY(2px)
|
||||||
|
|
||||||
|
&.active
|
||||||
|
transform: translateX(1px) translateY(4px)
|
||||||
|
|
||||||
|
&.active > span
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
&:nth-of-type(1)
|
||||||
|
transform: rotate(45deg) translate(-2px, -1px)
|
||||||
|
|
||||||
|
&:nth-of-type(2)
|
||||||
|
opacity: 0
|
||||||
|
transform: rotate(0deg) scale(0.2, 0.2)
|
||||||
|
|
||||||
|
&:nth-of-type(3)
|
||||||
|
transform: rotate(-45deg) translate(0, -1px)
|
Loading…
Reference in New Issue
Block a user