import './WJsonviewTree.css'
/**
* 前端通過元素展示Json樹狀資料,因 [Fork: {@link https://github.com/pgrabovets/json-view json-view}] 沒有加入預先展開數據功能,自己下載來修改
*
* @export
* @param {Object} jsonObj 輸入Json物件
* @param {Element} rootElem 輸入初始化元素
* @param {Object} [option={}] 輸入設定物件,預設為空物件
* @param {Boolean} [option.expanded=false] 輸入是否預先展開,預設為false
*/
function WJsonviewTree(jsonObj, rootElem, option = {}) {
//default expanded
let _expanded = false
//Init
Init(jsonObj, rootElem, option)
/**
* Initialize
*/
function Init(jsonObj, rootElem, option) {
//get expanded
if (option) {
_expanded = option['expanded'] === true
}
//clear
rootElem.innerHTML = ''
//add class
rootElem.classList.add('CompCssDJsonViewTree')
//render
let tree = createTree(jsonObj)
render(tree, rootElem)
}
/**
* Create html element
* @param {String} type html element
* @param {Object} config
*/
function createElement(type, config) {
let htmlElement = document.createElement(type)
if (config === undefined) {
return htmlElement
}
if (config.className) {
htmlElement.className = config.className
}
if (config.content) {
htmlElement.textContent = config.content
}
if (config.children) {
config.children.forEach((el) => {
if (el !== null) {
htmlElement.appendChild(el)
}
})
}
return htmlElement
}
/**
* Create expanded element
* @param {Object} node
* @return {HTMLElement}
*/
function createExpandedElement(node) {
let iElem = createElement('i')
if (node.expanded) {
iElem.className = 'wicon w-caret-down'
}
else {
iElem.className = 'wicon w-caret-right'
}
let caretElem = createElement('div', {
className: 'wjv-caret-icon',
children: [iElem],
})
let handleClick = node.toggle.bind(node)
caretElem.addEventListener('click', handleClick)
let indexElem = createElement('div', {
className: 'wjv-json-index',
content: node.key,
})
let typeElem = createElement('div', {
className: 'wjv-json-type',
content: node.type,
})
let keyElem = createElement('div', {
className: 'wjv-json-key',
content: node.key,
})
let sizeElem = createElement('div', {
className: 'wjv-json-size'
})
if (node.type === 'array') {
sizeElem.innerText = '[' + node.children.length + ']'
}
else if (node.type === 'object') {
sizeElem.innerText = '{' + node.children.length + '}'
}
let lineChildren
if (node.key === null) {
lineChildren = [caretElem, typeElem, sizeElem]
}
else if (node.parent.type === 'array') {
lineChildren = [caretElem, indexElem, sizeElem]
}
else {
lineChildren = [caretElem, keyElem, sizeElem]
}
let lineElem = createElement('div', {
className: 'wjv-line',
children: lineChildren
})
if (node.depth > 0) {
//lineElem.style = 'margin-left: ' + node.depth * 24 + 'px;' //IE11 strict模式下無法指派, style為唯讀屬性
lineElem.setAttribute('style', 'margin-left: ' + node.depth * 24 + 'px;')
}
return lineElem
}
/**
* Create not expanded element
* @param {Object} node
* @return {HTMLElement}
*/
function createNotExpandedElement(node) {
let caretElem = createElement('div', {
className: 'wjv-empty-icon',
})
let keyElem = createElement('div', {
className: 'wjv-json-key',
content: node.key
})
let separatorElement = createElement('div', {
className: 'wjv-json-separator',
content: ':'
})
let valueType = ' wjv-json-' + typeof node.value
let valueContent = String(node.value)
let valueElement = createElement('div', {
className: 'wjv-json-value' + valueType,
content: valueContent
})
let lineElem = createElement('div', {
className: 'wjv-line',
children: [caretElem, keyElem, separatorElement, valueElement]
})
if (node.depth > 0) {
//lineElem.style = 'margin-left: ' + node.depth * 24 + 'px;' //IE11 strict模式下無法指派, style為唯讀屬性
lineElem.setAttribute('style', 'margin-left: ' + node.depth * 24 + 'px;')
}
return lineElem
}
/**
* Create tree node
* @return {Object}
*/
function createNode() {
return {
key: null,
parent: null,
value: null,
expanded: _expanded,
type: null,
children: null,
elem: null,
depth: 0,
setCaretIconRight() {
let icon = this.elem.querySelector('.wicon')
icon.classList.replace('w-caret-down', 'w-caret-right')
},
setCaretIconDown() {
let icon = this.elem.querySelector('.wicon')
icon.classList.replace('w-caret-right', 'w-caret-down')
},
hideChildren() {
if (this.children !== null) {
this.children.forEach((item) => {
item.elem.classList.add('wjv-json-hide')
if (item.expanded) {
item.hideChildren()
}
})
}
},
showChildren() {
if (this.children !== null) {
this.children.forEach((item) => {
item.elem.classList.remove('wjv-json-hide')
if (item.expanded) {
item.showChildren()
}
})
}
},
toggle: function() {
if (this.expanded) {
this.expanded = false
this.hideChildren()
this.setCaretIconRight()
}
else {
this.expanded = true
this.showChildren()
this.setCaretIconDown()
}
}
}
}
/**
* Return variable type
* @param {*} val
*/
function getType(val) {
let type = typeof val
if (Array.isArray(val)) {
type = 'array'
}
else if (val === null) {
type = 'null'
}
return type
}
/**
* Recursively traverse json object
* @param {Object} obj parsed json object
* @param {Object} parent of object tree
*/
function traverseObject(obj, parent) {
for (let key in obj) {
let child = createNode()
child.parent = parent
child.key = key
child.type = getType(obj[key])
child.depth = parent.depth + 1
child.expanded = _expanded
if (typeof obj[key] === 'object') {
child.children = []
parent.children.push(child)
traverseObject(obj[key], child)
child.elem = createExpandedElement(child)
}
else {
child.value = obj[key]
child.elem = createNotExpandedElement(child)
parent.children.push(child)
}
}
}
/**
* Create root of a tree
* @param {Object} obj Json object
* @return {Object}
*/
function createTree(obj) {
let tree = createNode()
tree.type = getType(obj)
tree.children = []
tree.expanded = _expanded
traverseObject(obj, tree)
tree.elem = createExpandedElement(tree)
return tree
}
/**
* Recursively traverse Tree object
* @param {Object} node
* @param {Callback} callback
*/
function traverseTree(node, callback) {
callback(node)
if (node.children !== null) {
node.children.forEach((item) => {
traverseTree(item, callback)
})
}
}
/**
* Render Tree object
* @param {Object} tree
* @param {String} rootElem
*/
function render(tree, rootElem) {
traverseTree(tree, (node) => {
if (!node.expanded) {
node.hideChildren()
}
rootElem.appendChild(node.elem)
})
}
}
export default WJsonviewTree