From 8b7711139399abe6cf8c03ba1ed1cc00bcf72782 Mon Sep 17 00:00:00 2001
From: jos
Date: Wed, 30 May 2018 21:54:25 +0200
Subject: [PATCH] Implemented advanced sorting modal (WIP)
---
HISTORY.md | 5 ++
package-lock.json | 5 ++
package.json | 3 +-
src/css/contextmenu.css | 44 ++++++++++
src/js/History.js | 4 +-
src/js/Node.js | 167 ++++++++++++++++++++++++++++---------
test/test_large_array.html | 7 +-
7 files changed, 190 insertions(+), 45 deletions(-)
diff --git a/HISTORY.md b/HISTORY.md
index 273634c..939a41d 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -3,6 +3,11 @@
https://github.com/josdejong/jsoneditor
+## not yet released, version 5.17.0
+
+- Implemented advanced sorting for arrays.
+
+
## 2018-05-23, version 5.16.0
- Better handling of JSON documents containing large arrays:
diff --git a/package-lock.json b/package-lock.json
index 40dffcb..b871020 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3676,6 +3676,11 @@
"integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=",
"dev": true
},
+ "picomodal": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/picomodal/-/picomodal-3.0.0.tgz",
+ "integrity": "sha1-+s0w9PvzSoCcHgTqUl8ATzmcC4I="
+ },
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
diff --git a/package.json b/package.json
index 2a4a3f1..463ee51 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,8 @@
"dependencies": {
"ajv": "5.5.2",
"brace": "0.11.0",
- "javascript-natural-sort": "0.7.1"
+ "javascript-natural-sort": "0.7.1",
+ "picomodal": "3.0.0"
},
"devDependencies": {
"gulp": "3.9.1",
diff --git a/src/css/contextmenu.css b/src/css/contextmenu.css
index 6f693e2..88f8225 100644
--- a/src/css/contextmenu.css
+++ b/src/css/contextmenu.css
@@ -260,3 +260,47 @@ div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon {
background-image: none;
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;
+}
\ No newline at end of file
diff --git a/src/js/History.js b/src/js/History.js
index fa4c9e9..a363700 100644
--- a/src/js/History.js
+++ b/src/js/History.js
@@ -124,15 +124,15 @@ function History (editor) {
'undo': function (params) {
var node = params.node;
node.hideChilds();
- node.sort = params.oldSort;
node.childs = params.oldChilds;
+ node._updateDomIndexes();
node.showChilds();
},
'redo': function (params) {
var node = params.node;
node.hideChilds();
- node.sort = params.newSort;
node.childs = params.newChilds;
+ node._updateDomIndexes();
node.showChilds();
}
}
diff --git a/src/js/Node.js b/src/js/Node.js
index b852abb..b0692c3 100644
--- a/src/js/Node.js
+++ b/src/js/Node.js
@@ -1,6 +1,7 @@
'use strict';
var naturalSort = require('javascript-natural-sort');
+var picoModal = require('picomodal');
var ContextMenu = require('./ContextMenu');
var appendNodeFactory = require('./appendNodeFactory');
var showMoreNodeFactory = require('./showMoreNodeFactory');
@@ -389,7 +390,7 @@ Node.prototype.setValue = function(value, type) {
// sort object keys
if (this.editor.options.sortObjectKeys === true) {
- this.sort('asc');
+ this.sort([], 'asc');
}
}
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'
* or 'array'.
+ * @param {String[]} path Path of the child value to be compared
* @param {String} direction Sorting direction. Available values: "asc", "desc"
* @private
*/
-Node.prototype.sort = function (direction) {
+Node.prototype.sort = function (path, direction) {
if (!this._hasChilds()) {
return;
}
- var order = (direction == 'desc') ? -1 : 1;
- var prop = (this.type == 'array') ? 'value': 'field';
- this.hideChilds();
+ this.hideChilds(); // sorting is faster when the childs are not attached to the dom
+ // copy the childs array (the old one will be kept for an undo action
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();
- // sort the arrays
- this.childs.sort(function (a, b) {
- return order * naturalSort(a[prop], b[prop]);
- });
- this.sortOrder = (order == 1) ? 'asc' : 'desc';
+ // sort the childs array
+ var order = (direction === 'desc') ? -1 : 1;
+
+ if (this.type === 'object') {
+ this.childs.sort(function (a, b) {
+ return order * naturalSort(a.field, b.field);
+ });
+ }
+ 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
this._updateDomIndexes();
@@ -3126,14 +3140,44 @@ Node.prototype.sort = function (direction) {
this.editor._onAction('sort', {
node: this,
oldChilds: oldChilds,
- oldSort: oldSortOrder,
- newChilds: this.childs,
- newSort: this.sortOrder
+ newChilds: this.childs
});
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
* @return {Array}
@@ -3530,32 +3574,13 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
}
if (this._hasChilds()) {
- var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc');
items.push({
text: translate('sort'),
title: translate('sortTitle') + this.type,
- className: 'jsoneditor-sort-' + direction,
+ className: 'jsoneditor-sort-asc',
click: function () {
- node.sort(direction);
- },
- submenu: [
- {
- text: translate('ascending'),
- className: 'jsoneditor-sort-asc',
- title: translate('ascendingTitle' , {type: this.type}),
- click: function () {
- node.sort('asc');
- }
- },
- {
- text: translate('descending'),
- className: 'jsoneditor-sort-desc',
- title: translate('descendingTitle' , {type: this.type}),
- click: function () {
- node.sort('desc');
- }
- }
- ]
+ node._showSortModal()
+ }
});
}
@@ -3693,6 +3718,72 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
menu.show(anchor, this.editor.content);
};
+/**
+ * Show advanced sorting modal
+ * @private
+ */
+Node.prototype._showSortModal = function () {
+ var node = this;
+
+ picoModal({
+ parent: this.editor.frame,
+ content: '',
+ 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
* @param {*} value
diff --git a/test/test_large_array.html b/test/test_large_array.html
index 40e9e1d..2ddf91c 100644
--- a/test/test_large_array.html
+++ b/test/test_large_array.html
@@ -33,9 +33,7 @@
editor.setMode(mode)
, try it in the console of your browser.
-
+