├── .gitignore ├── LICENSE ├── README.md ├── demo ├── example2.json └── index.html ├── dist └── jsonview.js ├── package.json ├── src ├── .babelrc ├── json-view.js ├── jsonview.scss └── utils │ ├── dom.js │ ├── getDataType.js │ └── traverseObject.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pavel Grabovets 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # json-view 2 | This is a javascript library for displaying json data into a DOM. [link to demo](http://pgrabovets.github.io/json-view/) 3 | 4 | ### Installation 5 | ```javascript 6 | npm install '@pgrabovets/json-view'; 7 | ``` 8 | 9 | ### How to use 10 | include jsonview.js from dist directory in your html page 11 | ```html 12 | 13 | ``` 14 | or you can use import 15 | ```javascript 16 | import jsonview from '@pgrabovets/json-view'; 17 | ``` 18 | 19 | get json data and render tree into DOM 20 | ```javascript 21 | // get json data 22 | const data = '{"name": "json-view","version": "1.0.0"}'; 23 | 24 | // create json tree object 25 | const tree = jsonview.create(data); 26 | 27 | // render tree into dom element 28 | jsonview.render(tree, document.querySelector('.tree')); 29 | 30 | // you can render json data without creating tree 31 | const tree = jsonview.renderJSON(data, document.querySelector('.tree')); 32 | ``` 33 | 34 | control methods 35 | ```javascript 36 | // expand tree 37 | jsonview.expand(tree); 38 | 39 | // collapse tree 40 | jsonview.collapse(tree); 41 | 42 | // traverse tree object 43 | jsonview.traverse(tree, function(node) { 44 | console.log(node); 45 | }); 46 | 47 | // function toggles between show or hide 48 | jsonview.toggleNode(tree); 49 | 50 | // destroy and unmount json tree from the dom 51 | jsonview.destroy(tree); 52 | ``` 53 | 54 | ### Example1 55 | ```html 56 | 57 | 58 | 59 | JSON VIEW 60 | 61 | 62 |
63 | 64 | 65 | 79 | 80 | 81 | 82 | ``` 83 | 84 | ### Example2 85 | ```javascript 86 | import jsonview from '@pgrabovets/json-view'; 87 | 88 | fetch('example2.json') 89 | .then((res)=> { 90 | return res.text(); 91 | }) 92 | .then((data) => { 93 | const tree = jsonview.create(data); 94 | jsonview.render(tree, document.querySelector('.root')); 95 | jsonview.expand(tree); 96 | }) 97 | .catch((err) => { 98 | console.log(err); 99 | }) 100 | ``` 101 | 102 | ### For development install dependencies and run scripts 103 | ``` 104 | $ npm install 105 | 106 | $ npm run serve 107 | $ npm run build 108 | 109 | open http://localhost:3000/ 110 | ``` 111 | -------------------------------------------------------------------------------- /demo/example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "givenName": "Vas", 3 | "surName": "Sudanagunta", 4 | "children": [ 5 | { 6 | "givenName": "Natalia", 7 | "age": 5 8 | }, 9 | { 10 | "givenName": "Aida", 11 | "age": 17 12 | } 13 | ], 14 | "address": { 15 | "state": "NY", 16 | "city": "Brooklyn", 17 | "street": "718 Marcus Garvey Ave" 18 | }, 19 | "emptyObject": {}, 20 | "prop-with-null": null, 21 | "prop-empty-string": "" 22 | } 23 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jsonview demo 5 | 6 | 7 |
8 | 9 | 10 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /dist/jsonview.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.jsonview=n():e.jsonview=n()}(self,(()=>(()=>{"use strict";var e={767:(e,n,t)=>{t.d(n,{Z:()=>s});var r=t(81),o=t.n(r),i=t(645),a=t.n(i)()(o());a.push([e.id,'.json-container{font-family:"Open Sans";font-size:16px;background-color:#fff;color:gray;box-sizing:border-box}.json-container .line{margin:4px 0;display:flex;justify-content:flex-start}.json-container .caret-icon{width:18px;text-align:center;cursor:pointer}.json-container .empty-icon{width:18px}.json-container .json-type{margin-right:4px;margin-left:4px}.json-container .json-key{color:#444;margin-right:4px;margin-left:4px}.json-container .json-index{margin-right:4px;margin-left:4px}.json-container .json-value{margin-left:8px}.json-container .json-number{color:#f9ae58}.json-container .json-boolean{color:#ec5f66}.json-container .json-string{color:#86b25c}.json-container .json-size{margin-right:4px;margin-left:4px}.json-container .hidden{display:none}.json-container .fas{display:inline-block;border-style:solid;width:0;height:0}.json-container .fa-caret-down{border-width:6px 5px 0 5px;border-color:gray rgba(0,0,0,0)}.json-container .fa-caret-right{border-width:5px 0 5px 6px;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) gray}',""]);const s=a},645:e=>{e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t="",r=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),r&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),r&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t})).join("")},n.i=function(e,t,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var a={};if(r)for(var s=0;s0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=i),t&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=t):d[2]=t),o&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=o):d[4]="".concat(o)),n.push(d))}},n}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var n=[];function t(e){for(var t=-1,r=0;r{var n={};e.exports=function(e,t){var r=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}},216:e=>{e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},565:(e,n,t)=>{e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},795:e=>{e.exports=function(e){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var r="";t.supports&&(r+="@supports (".concat(t.supports,") {")),t.media&&(r+="@media ".concat(t.media," {"));var o=void 0!==t.layer;o&&(r+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),r+=t.css,o&&(r+="}"),t.media&&(r+="}"),t.supports&&(r+="}");var i=t.sourceMap;i&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(i))))," */")),n.styleTagTransform(r,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}},589:e=>{e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}}},n={};function t(r){var o=n[r];if(void 0!==o)return o.exports;var i=n[r]={id:r,exports:{}};return e[r](i,i.exports,t),i.exports}t.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},t.d=(e,n)=>{for(var r in n)t.o(n,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},t.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.nc=void 0;var r={};return(()=>{t.r(r),t.d(r,{collapse:()=>A,create:()=>S,default:()=>H,destroy:()=>L,expand:()=>D,render:()=>w,renderJSON:()=>k,toggleNode:()=>N,traverse:()=>C});var e=t(379),n=t.n(e),o=t(795),i=t.n(o),a=t(569),s=t.n(a),c=t(565),l=t.n(c),d=t(216),u=t.n(d),p=t(589),f=t.n(p),v=t(767),y={};function h(e){return Array.isArray(e)?"array":null===e?"null":typeof e}function m(e){return document.createElement(e)}y.styleTagTransform=f(),y.setAttributes=l(),y.insert=s().bind(null,"head"),y.domAPI=i(),y.insertStyleElement=u(),n()(v.Z,y),v.Z&&v.Z.locals&&v.Z.locals;const g={HIDDEN:"hidden",CARET_ICON:"caret-icon",CARET_RIGHT:"fa-caret-right",CARET_DOWN:"fa-caret-down",ICON:"fas"};function x(e){e.children.forEach((e=>{e.el.classList.add(g.HIDDEN),e.isExpanded&&x(e)}))}function j(e){e.children.forEach((e=>{e.el.classList.remove(g.HIDDEN),e.isExpanded&&j(e)}))}function b(e){if(e.children.length>0){const n=e.el.querySelector("."+g.ICON);n&&n.classList.replace(g.CARET_RIGHT,g.CARET_DOWN)}}function E(e){if(e.children.length>0){const n=e.el.querySelector("."+g.ICON);n&&n.classList.replace(g.CARET_DOWN,g.CARET_RIGHT)}}function N(e){e.isExpanded?(e.isExpanded=!1,E(e),x(e)):(e.isExpanded=!0,b(e),j(e))}function C(e,n){n(e),e.children.length>0&&e.children.forEach((e=>{C(e,n)}))}function T(e={}){let n=e.hasOwnProperty("value")?e.value:null;return(e=>"object"===h(e)&&0===Object.keys(e).length)(n)&&(n="{}"),{key:e.key||null,parent:e.parent||null,value:n,isExpanded:e.isExpanded||!1,type:e.type||null,children:e.children||[],el:e.el||null,depth:e.depth||0,dispose:null}}function I(e,n){if("object"==typeof e)for(let t in e){const r=T({value:e[t],key:t,depth:n.depth+1,type:h(e[t]),parent:n});n.children.push(r),I(e[t],r)}}function O(e){return"string"==typeof e?JSON.parse(e):e}function S(e){const n=O(e),t=T({value:n,key:h(n),type:h(n)});return I(n,t),t}function k(e,n){const t=S(O(e));return w(t,n),t}function w(e,n){const t=function(){const e=m("div");return e.className="json-container",e}();C(e,(function(e){e.el=function(e){let n=m("div");const t=e=>{const n=e.children.length;return"array"===e.type?`[${n}]`:"object"===e.type?`{${n}}`:null};if(e.children.length>0){n.innerHTML=function(e={}){const{key:n,size:t}=e;return`\n
\n
\n
${n}
\n
${t}
\n
\n `}({key:e.key,size:t(e)});const r=n.querySelector("."+g.CARET_ICON);e.dispose=function(e,n,t){return e.addEventListener(n,t),()=>e.removeEventListener(n,t)}(r,"click",(()=>N(e)))}else n.innerHTML=function(e={}){const{key:n,value:t,type:r}=e;return`\n
\n
\n
${n}
\n
:
\n
${t}
\n
\n `}({key:e.key,value:""===e.value?'""':e.value,type:"{}"===e.value?"object":typeof e.value});const r=n.children[0];return null!==e.parent&&r.classList.add(g.HIDDEN),r.style="margin-left: "+18*e.depth+"px;",r}(e),t.appendChild(e.el)})),n.appendChild(t)}function D(e){C(e,(function(e){e.el.classList.remove(g.HIDDEN),e.isExpanded=!0,b(e)}))}function A(e){C(e,(function(n){n.isExpanded=!1,n.depth>e.depth&&n.el.classList.add(g.HIDDEN),E(n)}))}function L(e){var n;C(e,(e=>{e.dispose&&e.dispose()})),(n=e.el.parentNode).parentNode.removeChild(n)}const H={toggleNode:N,render:w,create:S,renderJSON:k,expand:D,collapse:A,traverse:C,destroy:L}})(),r})())); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pgrabovets/json-view", 3 | "version": "2.7.6", 4 | "description": "", 5 | "main": "dist/jsonview.js", 6 | "scripts": { 7 | "dev": "webpack serve", 8 | "serve": "webpack serve", 9 | "build": "webpack" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/pgrabovets/json-view.git" 14 | }, 15 | "keywords": [ 16 | "json" 17 | ], 18 | "author": "pavel grabovets", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "css-loader": "^6.5.1", 22 | "sass": "^1.45.2", 23 | "sass-loader": "^12.4.0", 24 | "style-loader": "^3.3.1", 25 | "webpack": "^5.65.0", 26 | "webpack-cli": "^4.9.1", 27 | "webpack-dev-server": "^4.7.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/env", {"modules": false}] 4 | ], 5 | "comments": false 6 | } 7 | -------------------------------------------------------------------------------- /src/json-view.js: -------------------------------------------------------------------------------- 1 | import './jsonview.scss'; 2 | 3 | import getDataType from './utils/getDataType'; 4 | import { listen, detach, element } from './utils/dom'; 5 | 6 | const classes = { 7 | HIDDEN: 'hidden', 8 | CARET_ICON: 'caret-icon', 9 | CARET_RIGHT: 'fa-caret-right', 10 | CARET_DOWN: 'fa-caret-down', 11 | ICON: 'fas' 12 | } 13 | 14 | function expandedTemplate(params = {}) { 15 | const { key, size } = params; 16 | return ` 17 |
18 |
19 |
${key}
20 |
${size}
21 |
22 | ` 23 | } 24 | 25 | function notExpandedTemplate(params = {}) { 26 | const { key, value, type } = params; 27 | return ` 28 |
29 |
30 |
${key}
31 |
:
32 |
${value}
33 |
34 | ` 35 | } 36 | 37 | function createContainerElement() { 38 | const el = element('div'); 39 | el.className = 'json-container'; 40 | return el; 41 | } 42 | 43 | function hideNodeChildren(node) { 44 | node.children.forEach((child) => { 45 | child.el.classList.add(classes.HIDDEN); 46 | if (child.isExpanded) { 47 | hideNodeChildren(child); 48 | } 49 | }); 50 | } 51 | 52 | function showNodeChildren(node) { 53 | node.children.forEach((child) => { 54 | child.el.classList.remove(classes.HIDDEN); 55 | if (child.isExpanded) { 56 | showNodeChildren(child); 57 | } 58 | }); 59 | } 60 | 61 | function setCaretIconDown(node) { 62 | if (node.children.length > 0) { 63 | const icon = node.el.querySelector('.' + classes.ICON); 64 | if (icon) { 65 | icon.classList.replace(classes.CARET_RIGHT, classes.CARET_DOWN); 66 | } 67 | } 68 | } 69 | 70 | function setCaretIconRight(node) { 71 | if (node.children.length > 0) { 72 | const icon = node.el.querySelector('.' + classes.ICON); 73 | if (icon) { 74 | icon.classList.replace(classes.CARET_DOWN, classes.CARET_RIGHT); 75 | } 76 | } 77 | } 78 | 79 | export function toggleNode(node) { 80 | if (node.isExpanded) { 81 | node.isExpanded = false; 82 | setCaretIconRight(node); 83 | hideNodeChildren(node); 84 | } else { 85 | node.isExpanded = true; 86 | setCaretIconDown(node); 87 | showNodeChildren(node); 88 | } 89 | } 90 | 91 | /** 92 | * Create node html element 93 | * @param {object} node 94 | * @return html element 95 | */ 96 | function createNodeElement(node) { 97 | let el = element('div'); 98 | 99 | const getSizeString = (node) => { 100 | const len = node.children.length; 101 | if (node.type === 'array') return `[${len}]`; 102 | if (node.type === 'object') return `{${len}}`; 103 | return null; 104 | } 105 | 106 | if (node.children.length > 0) { 107 | el.innerHTML = expandedTemplate({ 108 | key: node.key, 109 | size: getSizeString(node), 110 | }) 111 | const caretEl = el.querySelector('.' + classes.CARET_ICON); 112 | node.dispose = listen(caretEl, 'click', () => toggleNode(node)); 113 | } else { 114 | el.innerHTML = notExpandedTemplate({ 115 | key: node.key, 116 | value: node.value === "" ? '""' : node.value, 117 | type: node.value === '{}' ? 'object' : typeof node.value 118 | }) 119 | } 120 | 121 | const lineEl = el.children[0]; 122 | 123 | if (node.parent !== null) { 124 | lineEl.classList.add(classes.HIDDEN); 125 | } 126 | 127 | lineEl.style = 'margin-left: ' + node.depth * 18 + 'px;'; 128 | 129 | return lineEl; 130 | } 131 | 132 | /** 133 | * Recursively traverse Tree object 134 | * @param {Object} node 135 | * @param {Callback} callback 136 | */ 137 | export function traverse(node, callback) { 138 | callback(node); 139 | if (node.children.length > 0) { 140 | node.children.forEach((child) => { 141 | traverse(child, callback); 142 | }); 143 | } 144 | } 145 | 146 | /** 147 | * Create node object 148 | * @param {object} opt options 149 | * @return {object} 150 | */ 151 | function createNode(opt = {}) { 152 | const isEmptyObject = (value) => { 153 | return ( 154 | getDataType(value) === 'object' && 155 | Object.keys(value).length === 0 156 | ) 157 | } 158 | 159 | let value = opt.hasOwnProperty('value') ? opt.value : null; 160 | 161 | if (isEmptyObject(value)) { 162 | value = "{}"; 163 | } 164 | 165 | return { 166 | key: opt.key || null, 167 | parent: opt.parent || null, 168 | value: value, 169 | isExpanded: opt.isExpanded || false, 170 | type: opt.type || null, 171 | children: opt.children || [], 172 | el: opt.el || null, 173 | depth: opt.depth || 0, 174 | dispose: null 175 | } 176 | } 177 | 178 | /** 179 | * Create subnode for node 180 | * @param {object} Json data 181 | * @param {object} node 182 | */ 183 | function createSubnode(data, node) { 184 | if (typeof data === 'object') { 185 | for (let key in data) { 186 | const child = createNode({ 187 | value: data[key], 188 | key: key, 189 | depth: node.depth + 1, 190 | type: getDataType(data[key]), 191 | parent: node, 192 | }); 193 | node.children.push(child); 194 | createSubnode(data[key], child); 195 | } 196 | } 197 | } 198 | 199 | function getJsonObject(data) { 200 | return typeof data === 'string' ? JSON.parse(data) : data; 201 | } 202 | 203 | /** 204 | * Create tree 205 | * @param {object | string} jsonData 206 | * @return {object} 207 | */ 208 | export function create(jsonData) { 209 | const parsedData = getJsonObject(jsonData); 210 | const rootNode = createNode({ 211 | value: parsedData, 212 | key: getDataType(parsedData), 213 | type: getDataType(parsedData), 214 | }); 215 | createSubnode(parsedData, rootNode); 216 | return rootNode; 217 | } 218 | 219 | /** 220 | * Render JSON string into DOM container 221 | * @param {string | object} jsonData 222 | * @param {htmlElement} targetElement 223 | * @return {object} tree 224 | */ 225 | export function renderJSON(jsonData, targetElement) { 226 | const parsedData = getJsonObject(jsonData); 227 | const tree = create(parsedData); 228 | render(tree, targetElement); 229 | return tree; 230 | } 231 | 232 | /** 233 | * Render tree into DOM container 234 | * @param {object} tree 235 | * @param {htmlElement} targetElement 236 | */ 237 | export function render(tree, targetElement) { 238 | const containerEl = createContainerElement(); 239 | 240 | traverse(tree, function(node) { 241 | node.el = createNodeElement(node); 242 | containerEl.appendChild(node.el); 243 | }); 244 | 245 | targetElement.appendChild(containerEl); 246 | } 247 | 248 | export function expand(node) { 249 | traverse(node, function(child) { 250 | child.el.classList.remove(classes.HIDDEN); 251 | child.isExpanded = true; 252 | setCaretIconDown(child); 253 | }); 254 | } 255 | 256 | export function collapse(node) { 257 | traverse(node, function(child) { 258 | child.isExpanded = false; 259 | if (child.depth > node.depth) child.el.classList.add(classes.HIDDEN); 260 | setCaretIconRight(child); 261 | }); 262 | } 263 | 264 | export function destroy(tree) { 265 | traverse(tree, (node) => { 266 | if (node.dispose) { 267 | node.dispose(); 268 | } 269 | }) 270 | detach(tree.el.parentNode); 271 | } 272 | 273 | /** 274 | * Export public interface 275 | */ 276 | export default { 277 | toggleNode, 278 | render, 279 | create, 280 | renderJSON, 281 | expand, 282 | collapse, 283 | traverse, 284 | destroy 285 | } 286 | -------------------------------------------------------------------------------- /src/jsonview.scss: -------------------------------------------------------------------------------- 1 | .json-container { 2 | font-family: 'Open Sans'; 3 | font-size: 16px; 4 | background-color: #fff; 5 | color: #808080; 6 | box-sizing: border-box; 7 | 8 | .line { 9 | margin: 4px 0; 10 | display: flex; 11 | justify-content: flex-start; 12 | } 13 | 14 | .caret-icon { 15 | width: 18px; 16 | text-align: center; 17 | cursor: pointer; 18 | } 19 | 20 | .empty-icon { 21 | width: 18px; 22 | } 23 | 24 | .json-type { 25 | margin-right: 4px; 26 | margin-left: 4px; 27 | } 28 | 29 | .json-key { 30 | color: #444; 31 | margin-right: 4px; 32 | margin-left: 4px; 33 | } 34 | 35 | .json-index { 36 | margin-right: 4px; 37 | margin-left: 4px; 38 | } 39 | 40 | .json-value { 41 | margin-left: 8px; 42 | } 43 | 44 | .json-number { 45 | color: #f9ae58; 46 | } 47 | 48 | .json-boolean { 49 | color: #ec5f66; 50 | } 51 | 52 | .json-string { 53 | color: #86b25c; 54 | } 55 | 56 | .json-size { 57 | margin-right: 4px; 58 | margin-left: 4px; 59 | } 60 | 61 | .hidden { 62 | display: none; 63 | } 64 | 65 | .fas { 66 | display: inline-block; 67 | border-style: solid; 68 | width: 0; 69 | height: 0; 70 | } 71 | 72 | .fa-caret-down { 73 | border-width: 6px 5px 0 5px; 74 | border-color: #808080 transparent; 75 | } 76 | 77 | .fa-caret-right { 78 | border-width: 5px 0 5px 6px; 79 | border-color: transparent transparent transparent #808080; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | export function element(name) { 2 | return document.createElement(name); 3 | } 4 | 5 | export function append(target, node) { 6 | target.appendChild(node); 7 | } 8 | 9 | export function listen(node, event, handler) { 10 | node.addEventListener(event, handler); 11 | return () => node.removeEventListener(event, handler); 12 | } 13 | 14 | export function detach(node) { 15 | node.parentNode.removeChild(node); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/getDataType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get value data type 3 | * @param {*} data 4 | */ 5 | export default function getDataType(val) { 6 | if (Array.isArray(val)) return 'array'; 7 | if (val === null) return 'null'; 8 | return typeof val; 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/traverseObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Recursively traverse json object 3 | * @param {object} target 4 | * @param {function} callback 5 | */ 6 | function traverseObject(target, callback) { 7 | callback(target); 8 | if (typeof target === 'object') { 9 | for (let key in target) { 10 | traverseObject(target[key], callback); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | entry: './src/json-view.js', 6 | output: { 7 | filename: 'jsonview.js', 8 | library: { 9 | name: 'jsonview', 10 | type: 'umd' 11 | }, 12 | path: path.resolve(__dirname, 'dist'), 13 | }, 14 | devServer: { 15 | static: path.join(__dirname, 'demo'), 16 | port: 3000, 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.s[ac]ss$/i, 22 | use: [ 23 | "style-loader", 24 | "css-loader", 25 | "sass-loader", 26 | ], 27 | }, 28 | ], 29 | }, 30 | }; 31 | --------------------------------------------------------------------------------