Vue Tree Menu
A nice Vue.js Multi-Level Tree Menu.
Made with
Html
Css
Vue.js
Author
Béla Varga
Html
<div class="container center" id="app">
<ul class="cd-accordion-menu">
<item :model="treeData"></item>
</ul>
</div>
Css
*, *::after, *::before {
box-sizing: border-box;
}
body {
color: #fff;
background: #949c4e;
background: linear-gradient(
115deg,
rgba(86, 216, 228, 1) 10%,
rgba(159, 1, 234, 1) 90%
);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial,
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
}
html,
body,
.container {
min-height: 100vh;
}
.center {
display: flex;
justify-content: center;
align-items: center;
}
ol, ul {
list-style: none;
padding: 0px;
}
a {
color: #a0f6aa;
text-decoration: none;
}
h1 {
text-align: center;
width: 90%;
margin: 2em auto 0;
color: #507b55;
font-weight: bold;
}
.cd-accordion-menu {
width: 90%;
max-width: 600px;
margin: 4em auto;
background: rgba(0, 0, 0, 0.8);
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.3);
}
.cd-accordion-menu li {
user-select: none;
}
.cd-accordion-menu li span {
float: right;
}
.cd-accordion-menu label, .cd-accordion-menu a {
position: relative;
display: block;
padding: 18px 18px 18px 45px;
box-shadow: inset 0 -1px #000;
color: #ffffff;
}
.no-touch .cd-accordion-menu label:hover,
.no-touch .cd-accordion-menu a:hover {
background: #52565d;
}
.cd-accordion-menu label::before,
.cd-accordion-menu label::after,
.cd-accordion-menu a::after {
/* icons */
content: '';
display: inline-block;
width: 16px;
height: 16px;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.cd-accordion-menu label {
cursor: pointer;
}
.cd-accordion-menu label span {
float: right;
color: #828282;
}
.cd-accordion-menu li.file > label::before {
content: "\f15b";
}
.cd-accordion-menu li.folder > label::before {
content: "\f054";
}
.cd-accordion-menu li.file > label::before {
content: "\f15b";
}
.cd-accordion-menu li.add > label::before {
content: "\f067";
}
.cd-accordion-menu label::before {
/* arrow icon */
font: normal normal normal 14px/1 FontAwesome;
left: 18px;
transform: translateY(-50%);
transition: transform 0.3s;
}
.cd-accordion-menu label.open::before {
transform: translateY(-25%) rotate(90deg);
}
.cd-accordion-menu ul label,
.cd-accordion-menu ul a {
background: #000;
box-shadow: inset 0 -1px #141617;
padding-left: 82px;
}
.no-touch .cd-accordion-menu ul label:hover,
.no-touch .cd-accordion-menu ul a:hover {
background: #3c3f45;
}
.cd-accordion-menu > li:last-of-type > label,
.cd-accordion-menu > li:last-of-type > a,
.cd-accordion-menu > li > ul > li:last-of-type label,
.cd-accordion-menu > li > ul > li:last-of-type a {
box-shadow: none;
}
.cd-accordion-menu ul label::before {
left: 36px;
}
.cd-accordion-menu ul label::after,
.cd-accordion-menu ul a::after {
left: 59px;
}
.cd-accordion-menu ul ul label,
.cd-accordion-menu ul ul a {
padding-left: 100px;
}
.cd-accordion-menu ul ul label::before {
left: 54px;
}
.cd-accordion-menu ul ul label::after,
.cd-accordion-menu ul ul a::after {
left: 77px;
}
.cd-accordion-menu ul ul ul label,
.cd-accordion-menu ul ul ul a {
padding-left: 118px;
}
.cd-accordion-menu ul ul ul label::before {
left: 72px;
}
.cd-accordion-menu ul ul ul label::after,
.cd-accordion-menu ul ul ul a::after {
left: 95px;
}
Javascript
var treeData = {
name: "Group",
children: [
{
name: "Sub Group",
children: [{ name: "Item" }, { name: "Item" }]
},
{ name: "Item" }
]
};
Vue.component("item", {
template: `<li :class="[isFolder ? 'folder' : 'file']">
<label
:class="{'open': open}"
@click="toggle"
@dblclick="changeType">
{{ model.name }}
<span v-if="isFolder">{{ isFolder ? model.children.length : '' }}</span>
</label>
<ul v-show="open" v-if="isFolder" :class="{'open': open}">
<item
v-for="(model, index) in model.children"
:key="index"
:model="model">
</item>
<li class="add" @click="addChild"><label>Add New Item</label></li>
</ul>
</li>`,
props: {
model: Object
},
data: function() {
return {
open: false
};
},
computed: {
isFolder: function() {
return this.model.children && this.model.children.length;
}
},
methods: {
toggle: function() {
if (this.isFolder) {
this.open = !this.open;
}
},
changeType: function() {
if (!this.isFolder) {
Vue.set(this.model, "children", []);
this.addChild();
this.open = true;
}
},
addChild: function() {
this.model.children.push({
name: "New Item"
});
}
}
});
new Vue({
el: "#app",
data: {
treeData: treeData
}
});
Demo
See the Pen Vue Tree Menu by Béla Varga (@netzzwerg) on CodePen.