added an ability to set custom css classes for Node's field and value (#604)

* update for gulp 4.0.0

* added an ability to set custom css classes for Node's field and value

* v5.9.8-beta

* wip

* wip

* wip

* wip

* wip

* wip

* cleanup

* reverted ./dist/ files

* cleanup

* reverted package-lock.json

* updated onClassName signature

* recursive node class name update on change

* add/remove css classes on node

* example fix

* merged remote

* removed yarn.lock

* changes get reflected on both sides of the diff view in example 20

* refactored and cleaned up example 20
This commit is contained in:
Pawel Raszewski 2019-01-21 14:42:43 -05:00 committed by Jos de Jong
parent 160c82f378
commit 1c5d7d71ac
5 changed files with 225 additions and 5 deletions

View File

@ -0,0 +1,175 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
body {
font: 10.5pt arial;
color: #4d4d4d;
line-height: 150%;
width: 500px;
padding-left: 40px;
}
code {
background-color: #f5f5f5;
}
#containerLeft {
width: 500px;
height: 500px;
float: left;
}
#containerRight {
width: 500px;
height: 500px;
float: right;
}
#wrapper {
width: 1000px;
}
#containerRight .different_element {
background-color: greenyellow !important;
}
#containerLeft .different_element {
background-color: violet !important;
}
</style>
</head>
<body>
<p>
JSON Diff
</p>
<div id="wrapper">
<div id="containerLeft"></div>
<div id="containerRight"></div>
</div>
<script>
var containerLeft, containerRight, options, json;
containerLeft = document.getElementById('containerLeft');
containerRight = document.getElementById('containerRight');
function findNodeInJson(json, path){
if(!json || path.length ===0) {
return {field: undefined, value: undefined}
}
var first = path[0];
var remainingPath = path.slice(1);
if(remainingPath.length === 0) {
return {field: (typeof json[first] !== undefined ? first : undefined), value: json[first]};
} else {
return findNodeInJson(json[first], remainingPath)
}
}
optionsLeft = {
mode: 'tree',
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
onError: function (err) {
alert(err.toString());
},
onClassName: function({ path, field, value }) {
var thisNode = findNodeInJson(jsonLeft, path);
var oppositeNode = findNodeInJson(jsonRight, path);
// this is not a proper way of comparing objects/values but's it's good enough for this example
var isValueEqual = thisNode.value == oppositeNode.value;
if(Array.isArray(thisNode.value) && Array.isArray(oppositeNode.value)) {
isValueEqual = thisNode.value.every(function (e) {
return oppositeNode.value.includes(e);
})
}
if (thisNode.field === oppositeNode.field && isValueEqual) {
return 'the_same_element';
} else {
return 'different_element'
}
},
onChangeJSON: function (j) {
jsonLeft = j;
window.editorRight.refresh()
}
};
optionsRight = {
mode: 'tree',
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
// name: "jsonContent",
onError: function (err) {
alert(err.toString());
},
onClassName: function({ path, field, value }) {
var thisNode = findNodeInJson(jsonRight, path);
var oppositeNode = findNodeInJson(jsonLeft, path);
// this is not a proper way of comparing objects/values but's it's good enough for this example
var isValueEqual = thisNode.value == oppositeNode.value;
if(Array.isArray(thisNode.value) && Array.isArray(oppositeNode.value)) {
isValueEqual = thisNode.value.every(function (e) {
return oppositeNode.value.includes(e);
})
}
if (thisNode.field === oppositeNode.field && isValueEqual) {
return 'the_same_element';
} else {
return 'different_element'
}
},
onChangeJSON: function (j) {
jsonRight = j;
window.editorLeft.refresh();
}
};
jsonLeft = {
"arrayOfArrays": [1, 2, 999, [3,4,5]],
"someField": true,
"boolean": true,
"htmlcode": '&quot;',
"escaped_unicode": '\\u20b9',
"unicode": '\u20b9,\uD83D\uDCA9',
"return": '\n',
"null": null,
"thisObjectDoesntExistOnTheRight" : {key: "value"},
"number": 123,
"object": {"a": "b","new":4, "c": "d", "e": [1, 2, 3]},
"string": "Hello World",
"url": "http://jsoneditoronline.org",
"[0]": "zero"
};
jsonRight = {
"arrayOfArrays": [1, 2, [3,4,5]],
"boolean": true,
"htmlcode": '&quot;',
"escaped_unicode": '\\u20b9',
"thisFieldDoesntExistOnTheLeft": 'foobar',
"unicode": '\u20b9,\uD83D\uDCA9',
"return": '\n',
"null": null,
"number": 123,
"object": {"a": "b", "c": "d", "e": [1, 2, 3]},
"string": "Hello World",
"url": "http://jsoneditoronline.org",
"[0]": "zero"
};
window.editorLeft = new JSONEditor(containerLeft, optionsLeft, jsonLeft);
window.editorRight = new JSONEditor(containerRight, optionsRight, jsonRight);
</script>
</body>
</html>

View File

@ -82,6 +82,12 @@ if (typeof Promise === 'undefined') {
* Only applicable for
* modes 'form', 'tree' and
* 'view'
* {function} onClassName Callback method, triggered
* when a Node DOM is rendered. Function returns
* a css class name to be set on a node.
* Only applicable for
* modes 'form', 'tree' and
* 'view'
* {Number} maxVisibleChilds Number of children allowed for a node
* in 'tree', 'view', or 'form' mode before
* the "show more/show all" buttons appear.
@ -168,7 +174,7 @@ JSONEditor.VALID_OPTIONS = [
'ace', 'theme', 'autocomplete',
'onChange', 'onChangeJSON', 'onChangeText',
'onEditable', 'onError', 'onEvent', 'onModeChange', 'onNodeName', 'onValidate',
'onSelectionChange', 'onTextSelectionChange',
'onSelectionChange', 'onTextSelectionChange', 'onClassName',
'colorPicker', 'onColorPicker',
'timestampTag',
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',

View File

@ -31,7 +31,7 @@ function Node (editor, params) {
this.editor = editor;
this.dom = {};
this.expanded = false;
if(params && (params instanceof Object)) {
this.setField(params.field, params.fieldEditable);
if ('value' in params) {
@ -913,7 +913,31 @@ Node.prototype.hideChilds = function(options) {
delete this.visibleChilds;
}
};
/**
* set custom css classes on a node
*/
Node.prototype._updateCssClassName = function () {
if(this.dom.field
&& this.editor
&& this.editor.options
&& typeof this.editor.options.onClassName ==='function'
&& this.dom.tree){
util.removeAllClassNames(this.dom.tree);
const addClasses = this.editor.options.onClassName({ path: this.getPath(), field: this.field, value: this.value }) || "";
util.addClassName(this.dom.tree, "jsoneditor-values " + addClasses);
}
}
Node.prototype.recursivelyUpdateCssClassesOnNodes = function () {
this._updateCssClassName();
if (this.childs !== 'undefined') {
var i;
for (i in this.childs) {
this.childs[i].recursivelyUpdateCssClassesOnNodes();
}
}
}
/**
* Goes through the path from the node to the root and ensures that it is expanded
@ -1649,7 +1673,7 @@ Node.prototype._updateDomValue = function () {
}
if (this.searchValue) {
classNames.push('jsoneditor-highlight');
}
}
domValue.className = classNames.join(' ');
@ -1820,7 +1844,7 @@ Node.prototype._deleteDomColor = function () {
*/
Node.prototype._updateDomField = function () {
var domField = this.dom.field;
if (domField) {
if (domField) {
// make backgound color lightgray when empty
var isEmpty = (String(this.field) == '' && this.parent.type != 'array');
if (isEmpty) {
@ -2484,6 +2508,8 @@ Node.prototype.updateDom = function (options) {
// update field and value
this._updateDomField();
this._updateDomValue();
this._updateCssClassName();
// update childs indexes
if (options && options.updateIndexes === true) {

View File

@ -550,13 +550,18 @@ treemode._onChange = function () {
}
}
// trigger the onClassName callback
if(this.options.onClassName) {
this.node.recursivelyUpdateCssClassesOnNodes();
}
// trigger the onNodeName callback
if (this.options.onNodeName && this.node.childs) {
try {
this.node.recursivelyUpdateNodeName();
} catch (err) {
console.error("Error in onNodeName callback: ", err);
}
}
}
};

View File

@ -361,6 +361,14 @@ exports.addClassName = function addClassName(elem, className) {
}
};
/**
* remove all classes from the given elements style
* @param {Element} elem
*/
exports.removeAllClassNames = function removeAllClassNames(elem) {
elem.className = "";
};
/**
* add a className to the given elements style
* @param {Element} elem