Implemented advanced sorting modal (WIP)
This commit is contained in:
parent
9eb9c4fe0b
commit
8b77111393
|
@ -3,6 +3,11 @@
|
||||||
https://github.com/josdejong/jsoneditor
|
https://github.com/josdejong/jsoneditor
|
||||||
|
|
||||||
|
|
||||||
|
## not yet released, version 5.17.0
|
||||||
|
|
||||||
|
- Implemented advanced sorting for arrays.
|
||||||
|
|
||||||
|
|
||||||
## 2018-05-23, version 5.16.0
|
## 2018-05-23, version 5.16.0
|
||||||
|
|
||||||
- Better handling of JSON documents containing large arrays:
|
- Better handling of JSON documents containing large arrays:
|
||||||
|
|
|
@ -3676,6 +3676,11 @@
|
||||||
"integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=",
|
"integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"picomodal": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomodal/-/picomodal-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-+s0w9PvzSoCcHgTqUl8ATzmcC4I="
|
||||||
|
},
|
||||||
"posix-character-classes": {
|
"posix-character-classes": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "5.5.2",
|
"ajv": "5.5.2",
|
||||||
"brace": "0.11.0",
|
"brace": "0.11.0",
|
||||||
"javascript-natural-sort": "0.7.1"
|
"javascript-natural-sort": "0.7.1",
|
||||||
|
"picomodal": "3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gulp": "3.9.1",
|
"gulp": "3.9.1",
|
||||||
|
|
|
@ -260,3 +260,47 @@ div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* pico modal styling */
|
||||||
|
|
||||||
|
.jsoneditor-modal-overlay {
|
||||||
|
position: absolute !important;
|
||||||
|
|
||||||
|
background: rgb(1,1,1) !important;
|
||||||
|
opacity: 0.3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsoneditor-modal {
|
||||||
|
position: absolute !important;
|
||||||
|
|
||||||
|
border-radius: 2px !important;
|
||||||
|
padding: 30px 20px 10px 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsoneditor-modal table td {
|
||||||
|
padding: 5px 0;
|
||||||
|
padding-right: 20px;
|
||||||
|
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 10pt;
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsoneditor-modal table td:first-child {
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsoneditor-modal table td.jsoneditor-modal-input {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsoneditor-modal select {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsoneditor-modal .pico-close {
|
||||||
|
background: none !important;
|
||||||
|
font-size: 24px !important;
|
||||||
|
}
|
|
@ -124,15 +124,15 @@ function History (editor) {
|
||||||
'undo': function (params) {
|
'undo': function (params) {
|
||||||
var node = params.node;
|
var node = params.node;
|
||||||
node.hideChilds();
|
node.hideChilds();
|
||||||
node.sort = params.oldSort;
|
|
||||||
node.childs = params.oldChilds;
|
node.childs = params.oldChilds;
|
||||||
|
node._updateDomIndexes();
|
||||||
node.showChilds();
|
node.showChilds();
|
||||||
},
|
},
|
||||||
'redo': function (params) {
|
'redo': function (params) {
|
||||||
var node = params.node;
|
var node = params.node;
|
||||||
node.hideChilds();
|
node.hideChilds();
|
||||||
node.sort = params.newSort;
|
|
||||||
node.childs = params.newChilds;
|
node.childs = params.newChilds;
|
||||||
|
node._updateDomIndexes();
|
||||||
node.showChilds();
|
node.showChilds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
159
src/js/Node.js
159
src/js/Node.js
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var naturalSort = require('javascript-natural-sort');
|
var naturalSort = require('javascript-natural-sort');
|
||||||
|
var picoModal = require('picomodal');
|
||||||
var ContextMenu = require('./ContextMenu');
|
var ContextMenu = require('./ContextMenu');
|
||||||
var appendNodeFactory = require('./appendNodeFactory');
|
var appendNodeFactory = require('./appendNodeFactory');
|
||||||
var showMoreNodeFactory = require('./showMoreNodeFactory');
|
var showMoreNodeFactory = require('./showMoreNodeFactory');
|
||||||
|
@ -389,7 +390,7 @@ Node.prototype.setValue = function(value, type) {
|
||||||
|
|
||||||
// sort object keys
|
// sort object keys
|
||||||
if (this.editor.options.sortObjectKeys === true) {
|
if (this.editor.options.sortObjectKeys === true) {
|
||||||
this.sort('asc');
|
this.sort([], 'asc');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -3096,29 +3097,42 @@ Node.prototype._onChangeType = function (newType) {
|
||||||
/**
|
/**
|
||||||
* Sort the child's of the node. Only applicable when the node has type 'object'
|
* Sort the child's of the node. Only applicable when the node has type 'object'
|
||||||
* or 'array'.
|
* or 'array'.
|
||||||
|
* @param {String[]} path Path of the child value to be compared
|
||||||
* @param {String} direction Sorting direction. Available values: "asc", "desc"
|
* @param {String} direction Sorting direction. Available values: "asc", "desc"
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Node.prototype.sort = function (direction) {
|
Node.prototype.sort = function (path, direction) {
|
||||||
if (!this._hasChilds()) {
|
if (!this._hasChilds()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var order = (direction == 'desc') ? -1 : 1;
|
this.hideChilds(); // sorting is faster when the childs are not attached to the dom
|
||||||
var prop = (this.type == 'array') ? 'value': 'field';
|
|
||||||
this.hideChilds();
|
|
||||||
|
|
||||||
|
// copy the childs array (the old one will be kept for an undo action
|
||||||
var oldChilds = this.childs;
|
var oldChilds = this.childs;
|
||||||
var oldSortOrder = this.sortOrder;
|
|
||||||
|
|
||||||
// copy the array (the old one will be kept for an undo action
|
|
||||||
this.childs = this.childs.concat();
|
this.childs = this.childs.concat();
|
||||||
|
|
||||||
// sort the arrays
|
// sort the childs array
|
||||||
|
var order = (direction === 'desc') ? -1 : 1;
|
||||||
|
|
||||||
|
if (this.type === 'object') {
|
||||||
this.childs.sort(function (a, b) {
|
this.childs.sort(function (a, b) {
|
||||||
return order * naturalSort(a[prop], b[prop]);
|
return order * naturalSort(a.field, b.field);
|
||||||
});
|
});
|
||||||
this.sortOrder = (order == 1) ? 'asc' : 'desc';
|
}
|
||||||
|
else { // this.type === 'array'
|
||||||
|
this.childs.sort(function (a, b) {
|
||||||
|
var valueA = a.getNestedChild(path).value;
|
||||||
|
var valueB = b.getNestedChild(path).value;
|
||||||
|
|
||||||
|
if (typeof valueA !== 'string' && typeof valueB !== 'string') {
|
||||||
|
// both values are a number, boolean, or null
|
||||||
|
return valueA > valueB ? order : valueA < valueB ? -order : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return order * naturalSort(valueA, valueB);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// update the index numbering
|
// update the index numbering
|
||||||
this._updateDomIndexes();
|
this._updateDomIndexes();
|
||||||
|
@ -3126,14 +3140,44 @@ Node.prototype.sort = function (direction) {
|
||||||
this.editor._onAction('sort', {
|
this.editor._onAction('sort', {
|
||||||
node: this,
|
node: this,
|
||||||
oldChilds: oldChilds,
|
oldChilds: oldChilds,
|
||||||
oldSort: oldSortOrder,
|
newChilds: this.childs
|
||||||
newChilds: this.childs,
|
|
||||||
newSort: this.sortOrder
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showChilds();
|
this.showChilds();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a nested child given a path with properties
|
||||||
|
* @param {String[]} path
|
||||||
|
* @returns {Node}
|
||||||
|
*/
|
||||||
|
Node.prototype.getNestedChild = function (path) {
|
||||||
|
var i = 0;
|
||||||
|
var child = this;
|
||||||
|
|
||||||
|
while (child && i < path.length) {
|
||||||
|
child = child.findChildByProperty(path[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a child by property name
|
||||||
|
* @param {string} prop
|
||||||
|
* @return {Node | undefined} Returns the child node when found, or undefined otherwise
|
||||||
|
*/
|
||||||
|
Node.prototype.findChildByProperty = function(prop) {
|
||||||
|
if (this.type !== 'object') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.childs.find(function (child) {
|
||||||
|
return child.field === prop;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the paths of the sortable child paths of this node
|
* Get the paths of the sortable child paths of this node
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
|
@ -3530,32 +3574,13 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._hasChilds()) {
|
if (this._hasChilds()) {
|
||||||
var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc');
|
|
||||||
items.push({
|
items.push({
|
||||||
text: translate('sort'),
|
text: translate('sort'),
|
||||||
title: translate('sortTitle') + this.type,
|
title: translate('sortTitle') + this.type,
|
||||||
className: 'jsoneditor-sort-' + direction,
|
|
||||||
click: function () {
|
|
||||||
node.sort(direction);
|
|
||||||
},
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
text: translate('ascending'),
|
|
||||||
className: 'jsoneditor-sort-asc',
|
className: 'jsoneditor-sort-asc',
|
||||||
title: translate('ascendingTitle' , {type: this.type}),
|
|
||||||
click: function () {
|
click: function () {
|
||||||
node.sort('asc');
|
node._showSortModal()
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
text: translate('descending'),
|
|
||||||
className: 'jsoneditor-sort-desc',
|
|
||||||
title: translate('descendingTitle' , {type: this.type}),
|
|
||||||
click: function () {
|
|
||||||
node.sort('desc');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3693,6 +3718,72 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
|
||||||
menu.show(anchor, this.editor.content);
|
menu.show(anchor, this.editor.content);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show advanced sorting modal
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Node.prototype._showSortModal = function () {
|
||||||
|
var node = this;
|
||||||
|
|
||||||
|
picoModal({
|
||||||
|
parent: this.editor.frame,
|
||||||
|
content: '<div class="pico-modal-contents">' +
|
||||||
|
'<form onsubmit="alert(1)">' +
|
||||||
|
'<table>' +
|
||||||
|
'<tbody>' +
|
||||||
|
'<tr>' +
|
||||||
|
' <td>Sort by:</td>' +
|
||||||
|
' <td class="jsoneditor-modal-input">' +
|
||||||
|
' <select id="sortBy">' +
|
||||||
|
' </select>' +
|
||||||
|
' </td>' +
|
||||||
|
'</tr>' +
|
||||||
|
' <tr>' +
|
||||||
|
' <td>Direction:</td>' +
|
||||||
|
' <td class="jsoneditor-modal-input">' +
|
||||||
|
' <select id="direction">' +
|
||||||
|
' <option value="asc" selected>asc</option>' +
|
||||||
|
' <option value="desc">desc</option>' +
|
||||||
|
' </select>' +
|
||||||
|
' </td>' +
|
||||||
|
'</tr>' +
|
||||||
|
'<tr>' +
|
||||||
|
'<td colspan="2" class="jsoneditor-modal-input">' +
|
||||||
|
' <button type="submit" id="ok">Sort</button>' +
|
||||||
|
'</td>' +
|
||||||
|
'</tr>' +
|
||||||
|
'</tbody>' +
|
||||||
|
'</table>' +
|
||||||
|
'</form>' +
|
||||||
|
'</div>',
|
||||||
|
overlayClass: 'jsoneditor-modal-overlay',
|
||||||
|
modalClass: 'jsoneditor-modal'
|
||||||
|
})
|
||||||
|
.afterCreate(function (modal) {
|
||||||
|
var sortBy = modal.modalElem().querySelector('#sortBy');
|
||||||
|
var direction = modal.modalElem().querySelector('#direction');
|
||||||
|
var ok = modal.modalElem().querySelector('#ok');
|
||||||
|
|
||||||
|
node.getSortablePaths().forEach(function (path) {
|
||||||
|
var pathStr = '.' + path.join('.');
|
||||||
|
var option = document.createElement('option');
|
||||||
|
option.text = pathStr;
|
||||||
|
option.value = pathStr;
|
||||||
|
sortBy.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
ok.onclick = function () {
|
||||||
|
modal.close();
|
||||||
|
|
||||||
|
var path = sortBy.value;
|
||||||
|
var pathArray = (path === '.') ? [] : path.split('.').slice(1);
|
||||||
|
|
||||||
|
node.sort(pathArray, direction.value)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the type of a value
|
* get the type of a value
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
|
|
|
@ -33,9 +33,7 @@
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
<div id="jsoneditor"></div>
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var container = document.getElementById('jsoneditor');
|
var container = document.getElementById('jsoneditor');
|
||||||
|
@ -70,7 +68,8 @@
|
||||||
location: {
|
location: {
|
||||||
latitude: 51 + i / 10000,
|
latitude: 51 + i / 10000,
|
||||||
longitude: 4 + i / 10000,
|
longitude: 4 + i / 10000,
|
||||||
}
|
},
|
||||||
|
random: Math.random()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue