├── .babelrc ├── .circleci └── config.yml ├── .editorconfig ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist └── json-tree.js ├── docs └── index.html ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── index.js └── json-tree.vue ├── test └── index.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ], 5 | "comments": false 6 | } 7 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | dist: 4 | docker: 5 | - image: circleci/node:8.9.4-stretch-browsers 6 | steps: 7 | - checkout 8 | - run: npm install 9 | - run: npm run dist 10 | workflows: 11 | version: 2 12 | build: 13 | jobs: 14 | - dist 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | .gitignore 4 | .git/ 5 | node_modules/ 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2019 Leo Deng 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-json-tree 2 | 3 | [![CircleCI](https://img.shields.io/circleci/project/myst729/vue-json-tree/master.svg)](https://circleci.com/gh/myst729/vue-json-tree/tree/master) 4 | [![NPM](https://img.shields.io/npm/v/vue-json-tree.svg)](https://www.npmjs.com/package/vue-json-tree) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/myst729/Vuelog/blob/master/LICENSE) 6 | 7 | > Vue component that renders JSON data in a collapsible tree structure. 8 | 9 | 10 | ## usage 11 | 12 | #### use in browsers 13 | 14 | - Include the CSS and JS along with Vue, so you get a `` component. 15 | 16 | ```html 17 | 18 | 19 | ``` 20 | 21 | - Instantiate the component with your data. 22 | 23 | ```html 24 |
25 | 34 | ``` 35 | 36 | #### use with **webpack** and **vue-loader** 37 | 38 | - Install the `vue-json-tree` package via NPM. 39 | 40 | ```bash 41 | npm install --save vue-json-tree 42 | ``` 43 | 44 | - Import the SFC (with CSS embedded) and register it as a component, either globally or in another component. 45 | 46 | ```js 47 | import JsonTree from 'vue-json-tree' 48 | Vue.component('json-tree', JsonTree) 49 | ``` 50 | 51 | ## props 52 | 53 | #### `raw` (`string`, optional) 54 | 55 | The data you want to present in the tree view. Must be a valid JSON string, otherwise it fails. 56 | 57 | #### `data` (`any`, optional) 58 | 59 | If your JSON data has already been parsed, bind this one instead. Must be something that can be produced by `JSON.parse()`. 60 | 61 | #### `level` (`number`, optional) 62 | 63 | Sometimes the data structure is very deep. You could set them to collapsed on load. By default all levels are expanded. 64 | 65 | ## demo 66 | 67 | https://myst729.github.io/vue-json-tree/ 68 | 69 | 70 | MIT © [**Leo**](https://myst729.github.io/) 71 | -------------------------------------------------------------------------------- /dist/json-tree.js: -------------------------------------------------------------------------------- 1 | !function(e){var n={};function t(r){if(n[r])return n[r].exports;var s=n[r]={i:r,l:!1,exports:{}};return e[r].call(s.exports,s,s.exports,t),s.l=!0,s.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var s in e)t.d(r,s,function(n){return e[n]}.bind(null,s));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=4)}([function(e,n,t){var r=t(2);"string"==typeof r&&(r=[[e.i,r,""]]),r.locals&&(e.exports=r.locals);(0,t(5).default)("649ee1f2",r,!1,{})},function(e,n,t){"use strict";var r=t(0);t.n(r).a},function(e,n,t){(n=t(3)(!1)).push([e.i,"\n.json-tree {\n color: #394359;\n display: flex;\n flex-direction: column;\n font-family: Menlo, Monaco, Consolas, monospace;\n font-size: 12px;\n line-height: 20px;\n}\n.json-tree-root {\n background-color: #f7f8f9;\n border-radius: 3px;\n margin: 2px 0;\n min-width: 560px;\n padding: 10px;\n}\n.json-tree-ending,\n.json-tree-row {\n border-radius: 2px;\n display: flex;\n}\n.json-tree-paired,\n.json-tree-row:hover {\n background-color: #bce2ff;\n}\n.json-tree-expando {\n cursor: pointer;\n}\n.json-tree-sign {\n font-weight: 700;\n}\n.json-tree-collapsed {\n color: gray;\n font-style: italic;\n}\n.json-tree-value {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.json-tree-value-string {\n color: #9aab3a;\n}\n.json-tree-value-boolean {\n color: #ff0080;\n}\n.json-tree-value-number {\n color: #4f7096;\n}\n.json-tree-value-null {\n color: #c7444a;\n}\n",""]),e.exports=n},function(e,n,t){"use strict";e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t=function(e,n){var t=e[1]||"",r=e[3];if(!r)return t;if(n&&"function"==typeof btoa){var s=(o=r,i=btoa(unescape(encodeURIComponent(JSON.stringify(o)))),c="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(i),"/*# ".concat(c," */")),a=r.sources.map((function(e){return"/*# sourceURL=".concat(r.sourceRoot||"").concat(e," */")}));return[t].concat(a).concat([s]).join("\n")}var o,i,c;return[t].join("\n")}(n,e);return n[2]?"@media ".concat(n[2]," {").concat(t,"}"):t})).join("")},n.i=function(e,t,r){"string"==typeof e&&(e=[[null,e,""]]);var s={};if(r)for(var a=0;a1&&void 0!==arguments[1]?arguments[1]:0,r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:void 0,o={depth:t,last:r,primitive:!0,key:JSON.stringify(a)};if("object"!==s(n))return Object.assign(o,{type:s(n),value:JSON.stringify(n)});if(null===n)return Object.assign(o,{type:"null",value:"null"});if(Array.isArray(n)){var i=n.map((function(r,s){return e(r,t+1,s===n.length-1)}));return Object.assign(o,{primitive:!1,type:"array",value:i})}var c=Object.keys(n),l=c.map((function(r,s){return e(n[r],t+1,s===c.length-1,r)}));return Object.assign(o,{primitive:!1,type:"object",value:l})}(e)}}},methods:{format:function(e){return e>1?"".concat(e," items"):e?"1 item":"no items"}},created:function(){this.expanded=this.parsed.deptht.parts.length&&(r.parts.length=t.parts.length)}else{var o=[];for(s=0;s 2 | 3 | 4 | 5 | vue-json-tree 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

vue-json-tree

19 |

20 | 21 | CircleCI 22 | 23 | 24 | NPM 25 | 26 | 27 | LICENSE 28 | 29 |

30 |

Vue component that renders JSON data in a collapsible tree structure.

31 |
32 | 33 |

@usage

34 |

> Use in browsers

35 |

1. Include the CSS and JS along with Vue, so you get a <json-tree> component.

36 |
<script src="https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>
 37 | <script src="https://unpkg.com/vue-json-tree@0.4.3/dist/json-tree.js"></script>
38 |

2. Instantiate the component with your data.

39 |
<div id="app"></div>
 40 | <script>
 41 |   new Vue({
 42 |     template: '<json-tree :raw="sample"></json-tree>',
 43 |     el: '#app',
 44 |     data: {
 45 |       sample: '{"foo": "bar"}'
 46 |     }
 47 |   })
 48 | </script>
49 |

> Use with webpack and vue-loader

50 |

1. Install the vue-json-tree package via NPM.

51 |
npm install --save vue-json-tree
52 |

2. Import the SFC (with CSS embedded) and register it as a component, either globally or in another component.

53 |
import JsonTree from 'vue-json-tree'
 54 | Vue.component('json-tree', JsonTree)
55 |
56 | 57 |

@props

58 |

> raw (string, optional)

59 |

The data you want to present in the tree view. Must be a valid JSON string, otherwise it fails.

60 |

> data (any, optional)

61 |

If your JSON data has already been parsed, bind this one instead. Must be something that can be produced by JSON.parse().

62 |

> level (number, optional)

63 |

Sometimes the data structure is very deep. You could set them to collapsed on load. By default all levels are expanded.

64 |
65 | 66 |

@demo

67 |
68 | 103 |
104 | 105 |

@development

106 |

> Want to fix bugs or implement new features?

107 |

1. Grab the source code from GitHub.

108 |
git clone git@github.com:myst729/vue-json-tree.git
109 |

2. Install dependencies via NPM.

110 |
npm install
111 |

3. Run in dev mode and start to hack.

112 |
npm run dev
113 |

4. Make sure you run the tests.

114 |
npm run test
115 |

5. Build the dist files.

116 |
npm run dist
117 |
118 | 119 |

MIT © Leo Deng 2019

120 |
121 | 122 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config') 2 | 3 | module.exports = config => { 4 | config.set({ 5 | browsers: ['ChromeHeadless'], 6 | frameworks: ['mocha', 'chai'], 7 | reporters: ['mocha'], 8 | files: ['./test/index.js'], 9 | preprocessors: { 10 | './test/index.js': ['webpack'] 11 | }, 12 | webpack: webpackConfig, 13 | webpackMiddleware: { 14 | noInfo: true 15 | } 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-json-tree", 3 | "description": "Vue component that renders JSON data in a collapsible tree structure.", 4 | "version": "0.4.3", 5 | "author": "Leo Deng", 6 | "license": "MIT", 7 | "homepage": "https://github.com/myst729/vue-json-tree", 8 | "keywords": [ 9 | "vue", 10 | "json", 11 | "tree" 12 | ], 13 | "main": "./src/json-tree.vue", 14 | "scripts": { 15 | "dev": "webpack --watch", 16 | "dist": "webpack", 17 | "test": "karma start --single-run" 18 | }, 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "@babel/core": "^7.9.0", 22 | "@babel/preset-env": "^7.9.5", 23 | "babel-loader": "^8.1.0", 24 | "chai": "^4.2.0", 25 | "css-loader": "^3.5.3", 26 | "karma": "^5.0.2", 27 | "karma-chai": "^0.1.0", 28 | "karma-chrome-launcher": "^3.1.0", 29 | "karma-mocha": "^2.0.0", 30 | "karma-mocha-reporter": "^2.2.5", 31 | "karma-webpack": "^4.0.2", 32 | "mocha": "^7.1.2", 33 | "terser-webpack-plugin": "^2.3.6", 34 | "vue": "^2.6.11", 35 | "vue-loader": "^15.9.1", 36 | "vue-style-loader": "^4.1.2", 37 | "vue-template-compiler": "^2.6.11", 38 | "webpack": "^4.43.0", 39 | "webpack-cli": "^3.3.11" 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "git+https://github.com/myst729/vue-json-tree.git" 44 | }, 45 | "bugs": { 46 | "url": "https://github.com/myst729/vue-json-tree/issues" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import component from './json-tree.vue' 2 | 3 | if (window.Vue) { 4 | Vue.component('json-tree', component) 5 | } 6 | -------------------------------------------------------------------------------- /src/json-tree.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 120 | 121 | 185 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { expect } from 'chai' 3 | import JsonTree from '../src/json-tree.vue' 4 | 5 | Vue.config.productionTip = false 6 | 7 | describe('Vue JSON Tree', () => { 8 | it('Should render JSON', done => { 9 | let vm = new Vue({ 10 | components: { JsonTree }, 11 | render: h => h('json-tree', { props: { raw: '{"foo": "bar"}' } }) 12 | }).$mount() 13 | 14 | vm.$nextTick(() => { 15 | const tree = vm.$el 16 | expect(tree.classList.contains('json-tree')).to.equal(true) 17 | expect(tree.classList.contains('json-tree-root')).to.equal(true) 18 | expect(tree.querySelectorAll('.json-tree-key').length).to.equal(1) 19 | expect(tree.querySelector('.json-tree-key').textContent).to.equal('"foo"') 20 | expect(tree.querySelectorAll('.json-tree-value-string').length).to.equal(1) 21 | expect(tree.querySelector('.json-tree-value-string').textContent).to.equal('"bar"') 22 | done() 23 | }) 24 | }) 25 | 26 | it('Should render JavaScript data', done => { 27 | let vm = new Vue({ 28 | components: { JsonTree }, 29 | render: h => h('json-tree', { props: { data: { baz: 7 } } }) 30 | }).$mount() 31 | 32 | vm.$nextTick(() => { 33 | const tree = vm.$el 34 | expect(tree.classList.contains('json-tree')).to.equal(true) 35 | expect(tree.classList.contains('json-tree-root')).to.equal(true) 36 | expect(tree.querySelectorAll('.json-tree-key').length).to.equal(1) 37 | expect(tree.querySelector('.json-tree-key').textContent).to.equal('"baz"') 38 | expect(tree.querySelectorAll('.json-tree-value-number').length).to.equal(1) 39 | expect(tree.querySelector('.json-tree-value-number').textContent).to.equal('7') 40 | done() 41 | }) 42 | }) 43 | 44 | it('Should render collapsed branch', done => { 45 | let data = { foo: { bar: { baz: [ 1, [ true, false ] ] } } } 46 | let vm = new Vue({ 47 | components: { JsonTree }, 48 | render: h => h('json-tree', { props: { data, level: 2 } }) 49 | }).$mount() 50 | 51 | Vue.nextTick(() => { 52 | const tree = vm.$el 53 | const collapsible = tree.querySelectorAll('.json-tree-collapsed') 54 | const deeper = tree.querySelectorAll('.json-tree-deeper') 55 | const collapsed = Array.from(collapsible).filter(el => el.style.display !== 'none') 56 | const expanded = Array.from(deeper).filter(el => el.style.display !== 'none') 57 | expect(tree.classList.contains('json-tree')).to.equal(true) 58 | expect(tree.classList.contains('json-tree-root')).to.equal(true) 59 | expect(collapsible.length).to.equal(5) 60 | expect(deeper.length).to.equal(5) 61 | expect(collapsed.length).to.equal(3) 62 | expect(expanded.length).to.equal(2) 63 | done() 64 | }) 65 | }) 66 | 67 | it('Should warn no data', done => { 68 | let vm = new Vue({ 69 | components: { JsonTree }, 70 | render: h => h('json-tree') 71 | }).$mount() 72 | 73 | vm.$nextTick(() => { 74 | const tree = vm.$el 75 | expect(tree.classList.contains('json-tree')).to.equal(true) 76 | expect(tree.classList.contains('json-tree-root')).to.equal(true) 77 | expect(tree.innerText.trim()).to.equal('"[Vue JSON Tree] No data passed."') 78 | done() 79 | }) 80 | }) 81 | 82 | it('Should warn invalid data', done => { 83 | let vm = new Vue({ 84 | components: { JsonTree }, 85 | render: h => h('json-tree', { props: { raw: 'invalid' } }) 86 | }).$mount() 87 | 88 | vm.$nextTick(() => { 89 | const tree = vm.$el 90 | expect(tree.classList.contains('json-tree')).to.equal(true) 91 | expect(tree.classList.contains('json-tree-root')).to.equal(true) 92 | expect(tree.innerText.trim()).to.equal('"[Vue JSON Tree] Invalid raw JSON."') 93 | done() 94 | }) 95 | }) 96 | }) 97 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const TerserPlugin = require('terser-webpack-plugin') 3 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 4 | 5 | module.exports = { 6 | entry: { 7 | app: './src/index.js' 8 | }, 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | filename: 'json-tree.js' 12 | }, 13 | devtool: false, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.vue$/, 18 | loader: 'vue-loader' 19 | }, 20 | { 21 | test: /\.js$/, 22 | loader: 'babel-loader', 23 | exclude: /node_modules/ 24 | }, 25 | { 26 | test: /\.css$/, 27 | use: [ 28 | 'vue-style-loader', 29 | 'css-loader' 30 | ] 31 | } 32 | ] 33 | }, 34 | optimization: { 35 | minimizer: [ 36 | new TerserPlugin({}) 37 | ], 38 | }, 39 | plugins: [ 40 | new VueLoaderPlugin() 41 | ] 42 | } 43 | --------------------------------------------------------------------------------