frontend: add collapsable sidebar
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
f8dacef309
commit
48b9e595ff
3 changed files with 218 additions and 2 deletions
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
<Sidebar/>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<nav class="navbar navbar-expand navbar-light navbar-bg">
|
<nav class="navbar navbar-expand navbar-light navbar-bg">
|
||||||
<a class="sidebar-toggle d-flex" @click="toggleSidebar">
|
<a class="sidebar-toggle d-flex" @click="toggleSidebar">
|
||||||
|
@ -14,11 +15,13 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Footer from "@/components/Footer.vue";
|
import Footer from "@/components/Footer.vue";
|
||||||
|
import Sidebar from "@/components/Sidebar.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BaseLayout',
|
name: 'BaseLayout',
|
||||||
components: {
|
components: {
|
||||||
Footer
|
Footer,
|
||||||
|
Sidebar
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
hideSearch: {
|
hideSearch: {
|
||||||
|
@ -29,6 +32,8 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleSidebar() {
|
toggleSidebar() {
|
||||||
|
closeAllDropdowns();
|
||||||
|
document.getElementById("sidebar").classList.toggle("collapsed");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
181
frontend/src/components/Sidebar.vue
Normal file
181
frontend/src/components/Sidebar.vue
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
<template>
|
||||||
|
<nav id="sidebar" class="sidebar">
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<router-link to="/" class="sidebar-brand">
|
||||||
|
<img src="/src/assets/icons/toolshed-48x48.png" alt="Toolshed Logo" class="align-middle logo mr-2 h-75">
|
||||||
|
<span class="align-middle">Toolshed</span>
|
||||||
|
</router-link>
|
||||||
|
<ul class="sidebar-nav">
|
||||||
|
<li class="sidebar-header">
|
||||||
|
Tools & Components
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as BIcons from "bootstrap-icons-vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Sidebar",
|
||||||
|
components: {
|
||||||
|
...BIcons
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sidebar {
|
||||||
|
min-width: 260px;
|
||||||
|
max-width: 260px;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar, .sidebar-content {
|
||||||
|
transition: margin-left .35s ease-in-out, left .35s ease-in-out, margin-right .35s ease-in-out, right .35s ease-in-out;
|
||||||
|
background: #222e3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
min-width: 260px;
|
||||||
|
max-width: 260px;
|
||||||
|
direction: ltr
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar, .sidebar-content {
|
||||||
|
transition: margin-left .35s ease-in-out, left .35s ease-in-out, margin-right .35s ease-in-out, right .35s ease-in-out;
|
||||||
|
background: #222e3c
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
padding-left: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
list-style: none;
|
||||||
|
flex-grow: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link i, .sidebar-link svg, a.sidebar-link i, a.sidebar-link svg {
|
||||||
|
margin-right: .75rem;
|
||||||
|
color: rgba(233, 236, 239, .5)
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item.active .sidebar-link:hover, .sidebar-item.active > .sidebar-link {
|
||||||
|
color: #e9ecef;
|
||||||
|
background: linear-gradient(90deg, rgba(59, 125, 221, .1), rgba(59, 125, 221, .0875) 50%, transparent);
|
||||||
|
border-left-color: #3b7ddd
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item.active .sidebar-link:hover i, .sidebar-item.active .sidebar-link:hover svg, .sidebar-item.active > .sidebar-link i, .sidebar-item.active > .sidebar-link svg {
|
||||||
|
color: #e9ecef
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-dropdown .sidebar-link {
|
||||||
|
padding: .625rem 1.5rem .625rem 3.25rem;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 90%;
|
||||||
|
border-left: 0;
|
||||||
|
color: #adb5bd;
|
||||||
|
background: transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-dropdown .sidebar-link:before {
|
||||||
|
content: "→";
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
left: -14px;
|
||||||
|
transition: all .1s ease;
|
||||||
|
transform: translateX(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-dropdown .sidebar-item .sidebar-link:hover {
|
||||||
|
font-weight: 400;
|
||||||
|
border-left: 0;
|
||||||
|
color: #e9ecef;
|
||||||
|
background: transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-dropdown .sidebar-item .sidebar-link:hover:hover:before {
|
||||||
|
transform: translateX(4px)
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-dropdown .sidebar-item.active .sidebar-link {
|
||||||
|
font-weight: 400;
|
||||||
|
border-left: 0;
|
||||||
|
color: #518be1;
|
||||||
|
background: transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar [data-toggle=collapse] {
|
||||||
|
position: relative
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar [data-toggle=collapse]:after {
|
||||||
|
content: " ";
|
||||||
|
border: solid;
|
||||||
|
border-width: 0 .075rem .075rem 0;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
position: absolute;
|
||||||
|
top: 1.2rem;
|
||||||
|
right: 1.5rem;
|
||||||
|
transition: all .2s ease-out
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar [aria-expanded=true]:after, .sidebar [data-toggle=collapse]:not(.collapsed):after {
|
||||||
|
transform: rotate(-135deg);
|
||||||
|
top: 1.4rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-brand {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
padding: 1.15rem 1.5rem;
|
||||||
|
display: block;
|
||||||
|
color: #f8f9fa
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-brand:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #f8f9fa
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-brand:focus {
|
||||||
|
outline: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
margin-left: -260px
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1px) and (max-width: 991.98px) {
|
||||||
|
.sidebar {
|
||||||
|
margin-left: -260px
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
margin-left: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
background: transparent;
|
||||||
|
padding: 1.5rem 1.5rem .375rem;
|
||||||
|
font-size: .75rem;
|
||||||
|
color: #ced4da
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,4 +13,34 @@ const app = createApp(App).use(BootstrapIconsPlugin);
|
||||||
_nacl.instantiate((nacl) => {
|
_nacl.instantiate((nacl) => {
|
||||||
window.nacl = nacl
|
window.nacl = nacl
|
||||||
app.use(router).mount('#app')
|
app.use(router).mount('#app')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.closeAllDropdowns = function () {
|
||||||
|
const dropdowns = document.getElementsByClassName("dropdown-menu");
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < dropdowns.length; i++) {
|
||||||
|
const openDropdown = dropdowns[i];
|
||||||
|
if (openDropdown.classList.contains('show')) {
|
||||||
|
openDropdown.classList.remove('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onclick = function (event) {
|
||||||
|
if (!event.target.matches('.dropdown-toggle *')
|
||||||
|
&& !event.target.matches('.dropdown-toggle')
|
||||||
|
&& !event.target.matches('.dropdown-menu *')
|
||||||
|
&& !event.target.matches('.dropdown-menu')) {
|
||||||
|
closeAllDropdowns();
|
||||||
|
}
|
||||||
|
if (!event.target.matches('.sidebar-toggle *')
|
||||||
|
&& !event.target.matches('.sidebar-toggle')
|
||||||
|
&& !event.target.matches('.sidebar *')
|
||||||
|
&& !event.target.matches('.sidebar')) {
|
||||||
|
const sidebar = document.getElementById("sidebar");
|
||||||
|
const marginLeft = parseInt(getComputedStyle(sidebar).marginLeft);
|
||||||
|
if (sidebar.classList.contains('collapsed') && marginLeft === 0) {
|
||||||
|
sidebar.classList.remove('collapsed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue