JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<div class="container" id="vue-root">
<div class="row">
<div class="col-xs-12">
<h1>A JSON Tree View Component in Vue.js</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<h2>
Simple JSON <small>opened until depth of 7</small>
</h2>
<div class="well">
<tree-view :data="sampleData" link=true max-depth="7" ></tree-view>>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<h2>
Complicated JSON <small>opened until depth of 3</small>
</h2>
<div class="well">
<tree-view :data="sampleData1" max-depth="3"></tree-view>
</div>
</div>
</div>
</div>
CSS
/* The Tree View should only fill out available space, scroll when
necessary.
*/
.tree-view-item {
font-family: monospace;
font-size: 14px;
margin-left: 18px;
}
.tree-view-wrapper {
overflow: auto;
}
/* Find the first nested node and override the indentation */
.tree-view-item-root > .tree-view-item-leaf > .tree-view-item {
margin-left: 0;
}
/* Root node should not be indented */
.tree-view-item-root {
margin-left: 0;
}
.tree-view-item-node {
cursor: pointer;
position: relative;
white-space: nowrap;
}
.tree-view-item-leaf {
white-space: nowrap;
}
.tree-view-item-key {
font-weight: bold;
}
.tree-view-item-key-with-chevron {
padding-left: 14px;
}
.tree-view-item-key-with-chevron.opened::before {
top:4px;
transform: rotate(90deg);
-webkit-transform: rotate(90deg);
}
.tree-view-item-key-with-chevron::before {
color: #444;
content: '\25b6';
font-size: 10px;
left: 1px;
position: absolute;
top: 3px;
transition: -webkit-transform .1s ease;
transition: transform .1s ease;
transition: transform .1s ease, -webkit-transform .1s ease;
-webkit-transition: -webkit-transform .1s ease;
}
.tree-view-item-hint {
color: #ccc
}
Babel + JSX
Vue.component("tree-view-item", Vue.extend({
name: "tree-view-item",
props: ["data", "max-depth", "current-depth"],
data: function(){
return {
open: this.currentDepth < this.maxDepth
}
},
methods: {
isOpen: function(){
return this.isRootObject(this.data) || this.open;
},
toggleOpen:function(){
this.open = !this.open;
},
isObject: function(value){
return value.type === 'object';
},
isArray: function(value){
return value.type === 'array';
},
isValue: function(value){
return value.type === 'value';
},
getKey: function(value){
if (_.isInteger(value.key)) {
return value.key+":";
} else {
return "\""+ value.key + "\":";
}
},
getValue: function(value){
if (_.isNumber(value.value)) {
return value.value
}
if (_.isNull(value.value)) {
return "null"
}
if (_.isString(value.value)) {}
return "\""+value.value+"\"";
},
isRootObject: function(value){
return value.isRoot;
}
},
template:`
<div class="tree-view-item">
<div v-if="isObject(data)" class="tree-view-item-leaf">
<div class="tree-view-item-node" @click.stop="toggleOpen()">
<span :class="{opened: isOpen()}" v-if="!isRootObject(data)" class="tree-view-item-key tree-view-item-key-with-chevron">{{getKey(data)}}</span>
<span class="tree-view-item-hint" v-show="!isOpen() && data.children.length === 1">{{data.children.length}} property</span>
<span class="tree-view-item-hint" v-show="!isOpen() && data.children.length !== 1">{{data.children.length}} properties</span>
</div>
<tree-view-item :max-depth="maxDepth" :current-depth="currentDepth+1" v-show="isOpen()" v-for="child in data.children" :data="child"></tree-view-item>
</div>
<div v-if="isArray(data)" class="tree-view-item-leaf">
<div class="tree-view-item-node" @click.stop="toggleOpen()">
...