mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-28 16:06:40 +01:00
Menu resizable (#206)
* Makes side menu resizable * Fixes hiding of side menu * Adds mobile menu * Adds missing file
This commit is contained in:
@@ -1,27 +1,32 @@
|
||||
<template lang="html">
|
||||
<div class="columns is-marginless">
|
||||
<side-menu></side-menu>
|
||||
<div class="column is-offset-3-tablet is-offset-2-widescreen is-9-tablet is-10-widescreen is-paddingless">
|
||||
<splitpanes>
|
||||
<pane>
|
||||
<router-view></router-view>
|
||||
</pane>
|
||||
<pane v-for="other in activeContainers" :key="other.id">
|
||||
<scrollable-view>
|
||||
<template v-slot:header>
|
||||
<div class="name columns is-marginless">
|
||||
<span class="column">{{ other.name }}</span>
|
||||
<span class="column is-narrow">
|
||||
<button class="delete is-medium" @click="removeActiveContainer(other)"></button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<log-viewer-with-source :id="other.id"></log-viewer-with-source>
|
||||
</scrollable-view>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</div>
|
||||
</div>
|
||||
<main>
|
||||
<mobile-menu v-if="mobileWidth"></mobile-menu>
|
||||
<splitpanes>
|
||||
<pane min-size="10" size="15" v-if="!mobileWidth">
|
||||
<side-menu></side-menu>
|
||||
</pane>
|
||||
<pane size="85">
|
||||
<splitpanes>
|
||||
<pane>
|
||||
<router-view></router-view>
|
||||
</pane>
|
||||
<pane v-for="other in activeContainers" :key="other.id">
|
||||
<scrollable-view>
|
||||
<template v-slot:header>
|
||||
<div class="name columns is-marginless">
|
||||
<span class="column">{{ other.name }}</span>
|
||||
<span class="column is-narrow">
|
||||
<button class="delete is-medium" @click="removeActiveContainer(other)"></button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<log-viewer-with-source :id="other.id"></log-viewer-with-source>
|
||||
</scrollable-view>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -31,12 +36,14 @@ import { Splitpanes, Pane } from "splitpanes";
|
||||
import LogViewerWithSource from "./components/LogViewerWithSource";
|
||||
import ScrollableView from "./components/ScrollableView";
|
||||
import SideMenu from "./components/SideMenu";
|
||||
import MobileMenu from "./components/MobileMenu";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
LogViewerWithSource,
|
||||
SideMenu,
|
||||
MobileMenu,
|
||||
ScrollableView,
|
||||
Splitpanes,
|
||||
Pane
|
||||
@@ -58,7 +65,7 @@ export default {
|
||||
this.title = `${this.containers.length} containers`;
|
||||
},
|
||||
computed: {
|
||||
...mapState(["containers", "activeContainers"])
|
||||
...mapState(["containers", "activeContainers", "mobileWidth"])
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
@@ -85,5 +92,8 @@ export default {
|
||||
::v-deep .splitpanes__splitter {
|
||||
min-width: 4px;
|
||||
background: #666;
|
||||
&:hover {
|
||||
background: rgb(255, 221, 87);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<App /> renders correctly 1`] = `
|
||||
<div
|
||||
class="columns is-marginless"
|
||||
>
|
||||
<side-menu-stub />
|
||||
<main>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="column is-offset-3-tablet is-offset-2-widescreen is-9-tablet is-10-widescreen is-paddingless"
|
||||
<splitpanes-stub
|
||||
dblclicksplitter="true"
|
||||
pushotherpanes="true"
|
||||
>
|
||||
<splitpanes-stub
|
||||
dblclicksplitter="true"
|
||||
pushotherpanes="true"
|
||||
<pane-stub
|
||||
maxsize="100"
|
||||
minsize="10"
|
||||
size="15"
|
||||
>
|
||||
<pane-stub
|
||||
maxsize="100"
|
||||
minsize="0"
|
||||
<side-menu-stub />
|
||||
</pane-stub>
|
||||
|
||||
<pane-stub
|
||||
maxsize="100"
|
||||
minsize="0"
|
||||
size="85"
|
||||
>
|
||||
<splitpanes-stub
|
||||
dblclicksplitter="true"
|
||||
pushotherpanes="true"
|
||||
>
|
||||
<router-view-stub />
|
||||
</pane-stub>
|
||||
|
||||
</splitpanes-stub>
|
||||
</div>
|
||||
</div>
|
||||
<pane-stub
|
||||
maxsize="100"
|
||||
minsize="0"
|
||||
>
|
||||
<router-view-stub />
|
||||
</pane-stub>
|
||||
|
||||
</splitpanes-stub>
|
||||
</pane-stub>
|
||||
</splitpanes-stub>
|
||||
</main>
|
||||
`;
|
||||
|
||||
85
assets/components/MobileMenu.vue
Normal file
85
assets/components/MobileMenu.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template lang="html">
|
||||
<aside>
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger burger is-white is-hidden-tablet is-pulled-right"
|
||||
@click="showNav = !showNav"
|
||||
:class="{ 'is-active': showNav }"
|
||||
>
|
||||
<span></span> <span></span> <span></span>
|
||||
</a>
|
||||
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
||||
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">Containers</p>
|
||||
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
|
||||
<li v-for="item in containers">
|
||||
<router-link
|
||||
:to="{ name: 'container', params: { id: item.id, name: item.name } }"
|
||||
active-class="is-active"
|
||||
:title="item.name"
|
||||
>
|
||||
<div class="hide-overflow">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters, mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
props: [],
|
||||
name: "MobileMenu",
|
||||
data() {
|
||||
return {
|
||||
showNav: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(["containers"]),
|
||||
activeContainersById() {
|
||||
return this.activeContainers.reduce((map, obj) => {
|
||||
map[obj.id] = obj;
|
||||
return map;
|
||||
}, {});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions({})
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
aside {
|
||||
padding: 1em;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #222;
|
||||
|
||||
.menu-label {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.hide-overflow {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.burger.is-white {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.is-hidden-mobile.is-active {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.navbar-burger {
|
||||
height: 2.35rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,16 +1,8 @@
|
||||
<template lang="html">
|
||||
<aside class="column menu is-3-tablet is-2-widescreen">
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger burger is-white is-hidden-tablet is-pulled-right"
|
||||
@click="showNav = !showNav"
|
||||
:class="{ 'is-active': showNav }"
|
||||
>
|
||||
<span></span> <span></span> <span></span>
|
||||
</a>
|
||||
<aside>
|
||||
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
||||
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">Containers</p>
|
||||
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
|
||||
<p class="menu-label is-hidden-mobile">Containers</p>
|
||||
<ul class="menu-list is-hidden-mobile">
|
||||
<li v-for="item in containers">
|
||||
<router-link
|
||||
:to="{ name: 'container', params: { id: item.id, name: item.name } }"
|
||||
@@ -40,16 +32,7 @@ export default {
|
||||
props: [],
|
||||
name: "SideMenu",
|
||||
data() {
|
||||
return {
|
||||
showNav: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
colorize: value =>
|
||||
ansiConvertor
|
||||
.toHtml(value)
|
||||
.replace("<mark>", "<mark>")
|
||||
.replace("</mark>", "</mark>")
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["containers", "activeContainers"]),
|
||||
@@ -69,30 +52,10 @@ export default {
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
aside {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
padding: 1em;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
|
||||
@media screen and (min-width: 769px) {
|
||||
& {
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
& {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
.hide-overflow {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -106,10 +69,6 @@ aside {
|
||||
.is-hidden-mobile.is-active {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.navbar-burger {
|
||||
height: 2.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.will-append-container.icon {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
// import storage from "store/dist/store.modern";
|
||||
|
||||
const mql = window.matchMedia("(max-width: 770px)");
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const state = {
|
||||
containers: [],
|
||||
activeContainers: [],
|
||||
searchFilter: null
|
||||
searchFilter: null,
|
||||
mobileWidth: mql.matches
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
@@ -22,6 +24,9 @@ const mutations = {
|
||||
},
|
||||
SET_SEARCH(state, filter) {
|
||||
state.searchFilter = filter;
|
||||
},
|
||||
SET_MOBILE_WIDTH(state, value) {
|
||||
state.mobileWidth = value;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,6 +50,8 @@ const getters = {};
|
||||
const es = new EventSource(`${BASE_PATH}/api/events/stream`);
|
||||
es.addEventListener("containers-changed", e => setTimeout(() => store.dispatch("FETCH_CONTAINERS"), 1000), false);
|
||||
|
||||
mql.addListener(e => store.commit("SET_MOBILE_WIDTH", e.matches));
|
||||
|
||||
const store = new Vuex.Store({
|
||||
strict: true,
|
||||
state,
|
||||
|
||||
Reference in New Issue
Block a user