CR Fix: tree selection - use serializable node as parameters

This commit is contained in:
Meir Rotstein 2018-04-02 01:40:10 +03:00
parent 7e90161057
commit 39e8d36f72
4 changed files with 102 additions and 36 deletions

View File

@ -216,7 +216,10 @@ Constructs a new JSONEditor.
callback signature should be:
```js
/**
* @param {Array<Node>} nodes selected nodes
* @typedef {{field: String, value: String|Object|Number|Boolean, path: Array.<String|Number>} SerializableNode
*
* @param {SerializableNode=} start
* @param {SerializableNode=} end
*/
function onSelectionChange(nodes) {
...
@ -374,9 +377,9 @@ Get the current selected nodes, Only applicable for mode 'tree'.
*Returns:*
- `{Array<Node>} nodes`
- `{{start:SerializableNode, end: SerializableNode}}`
#### `JSONEditor.setSelection(startNode, endNode)`
#### `JSONEditor.setSelection(start, end)`
Set selection for a range of nodes, Only applicable for mode 'tree'.
@ -386,13 +389,13 @@ Set selection for a range of nodes, Only applicable for mode 'tree'.
*Parameters:*
- `{Node} startNode`
- `{{path: Array.<String>}} start`
Node instance for selection start
Path for the start node
- `{Node} endNode`
- `{{path: Array.<String>}} end`
Node instance for selection end
Path for the end node
### Examples

View File

@ -33,15 +33,15 @@
<body>
<p>
Selection indication was done using the <code>on{Text/Node}SelectionChange</code> listeners.<br/>
Selection indication was done using the <code>on[Text]SelectionChange</code> listeners.<br/>
you can try the following calls in the console of your browser:<br/>
<code class="multiline">
// text and code modes:
editor.getTextSelection()
editor.setTextSelection(startPos,endPos)
editor.setTextSelection(startPos, endPos)
// tree mode:
editor.getSelection()
editor.setSelection(Node1,Node2)
editor.setSelection(startNode, endNode)
</code>
</p>
@ -52,7 +52,7 @@
<b>Text:</b><div id="selectedText"></div>
</div>
<div id="treeModeSelection">
<b>Selection: </b><span id="selectedCount"></span>
<b>Selection:</b>
<div id="selectedNodes"></div>
</div>
</form>
@ -91,15 +91,15 @@
var textEl = document.getElementById('selectedText');
textEl.innerHTML = text;
},
onSelectionChange: function(nodes) {
onSelectionChange: function(start, end) {
var nodesEl = document.getElementById('selectedNodes');
var nodesCountEl = document.getElementById('selectedCount');
nodesCountEl.innerHTML = nodes.length + ' nodes selected';
nodesEl.innerHTML = nodes.map(function(node) {
return node.field !== undefined
? node._escapeHTML(node.field)
: (isNaN(node.index) ? node.type : node.index);
}).join(", ");
nodesEl.innerHTML = '';
if (start) {
nodesEl.innerHTML = ('start: ' + JSON.stringify(start));
if (end) {
nodesEl.innerHTML += ('<br/>end: ' + JSON.stringify(end));
}
}
}
};

View File

@ -80,12 +80,7 @@ Node.prototype.getPath = function () {
var node = this;
var path = [];
while (node) {
var field = !node.parent
? undefined // do not add an (optional) field name of the root node
: (node.parent.type != 'array')
? node.field
: node.index;
var field = node.getName();
if (field !== undefined) {
path.unshift(field);
}
@ -94,6 +89,54 @@ Node.prototype.getPath = function () {
return path;
};
/**
* Get node serializable name
* @returns {String|Number}
*/
Node.prototype.getName = function () {
return !this.parent
? undefined // do not add an (optional) field name of the root node
: (this.parent.type != 'array')
? this.field
: this.index;
};
/**
* Find child node by serializable path
* @param {Array<String>} path
*/
Node.prototype.findNodeByPath = function (path) {
if (!path) {
return;
}
if (path.length == 0) {
return this;
}
if (path.length && this.childs && this.childs.length) {
for (var i=0; i < this.childs.length; ++i) {
if (path[0] === '' + this.childs[i].getName()) {
return this.childs[i].findNodeByPath(path.slice(1));
}
}
}
};
/**
* @typedef {{field: String, value: String|Object|Number|Boolean, path: Array.<String|Number>} SerializableNode
*
* Returns serializable representation for the node
* @return {SerializedNode}
*/
Node.prototype.serialize = function () {
return {
field: this.getField(),
value: this.getValue(),
path: this.getPath()
};
};
/**
* Find a Node from a JSON path like '.items[3].name'
* @param {string} jsonPath

View File

@ -1088,7 +1088,7 @@ treemode.deselect = function (clearStartAndEnd) {
if (selectionChanged) {
if (this._selectionChangedHandler) {
this._selectionChangedHandler([]);
this._selectionChangedHandler();
}
}
};
@ -1113,7 +1113,8 @@ treemode.select = function (nodes) {
});
if (this._selectionChangedHandler) {
this._selectionChangedHandler(nodes);
var selection = this.getSelection();
this._selectionChangedHandler(selection.start, selection.end);
}
}
};
@ -1340,10 +1341,18 @@ treemode.showContextMenu = function (anchor, onClose) {
/**
* Get current selected nodes
* @return {Array<Node>}
* @return {{start:SerializableNode, end: SerializableNode}} if no selection an empty object will be retured
*/
treemode.getSelection = function () {
return this.multiselection.nodes || [];
var selection = {};
if (this.multiselection.nodes && this.multiselection.nodes.length) {
selection.start = this.multiselection.nodes[0].serialize();
if (this.multiselection.nodes.length > 1) {
selection.end = this.multiselection.nodes[this.multiselection.nodes.length - 1].serialize();
}
}
return selection;
};
/**
@ -1351,7 +1360,8 @@ treemode.getSelection = function () {
* @param {selectionCallback} callback
*
* @callback selectionCallback
* @param {Array<Node>} nodes selected nodes
* @param {SerializableNode=} start
* @param {SerializableNode=} end
*/
treemode.onSelectionChange = function (callback) {
if (typeof callback === 'function') {
@ -1361,18 +1371,28 @@ treemode.onSelectionChange = function (callback) {
/**
* Select range of nodes.
* For selecting single node send only the first node parameter
* For clear selection do not send any parameter
* For selecting single node send only the start parameter
* For clear the selection do not send any parameter
* If the nodes are not from the same level the first common parent will be selected
* @param {Node} [startNode] node for selection start
* @param {Node} [endNode] node for selection end
* @param {{path: Array.<String>}} start object contains the path for selection start
* @param {{path: Array.<String>}=} end object contains the path for selection end
*/
treemode.setSelection = function (startNode, endNode) {
treemode.setSelection = function (start, end) {
// check for old usage
if (startNode.dom && startNode.range) {
if (start && start.dom && start.range) {
console.warn('setSelection/getSelection usage for text selection is depracated and should not be used, see documantaion for supported selection options');
this.setDomSelection(startNode);
}
var startNode, endNode;
if (start && start.path) {
startNode = this.node.findNodeByPath(start.path);
if (end && end.path) {
endNode = this.node.findNodeByPath(end.path);
}
}
var nodes = [];
if (startNode instanceof Node) {
if (endNode instanceof Node && endNode !== startNode) {