Implemented sort button in code/text mode
This commit is contained in:
parent
12dc0408e8
commit
74b816a554
|
@ -4292,8 +4292,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
|
|||
title: translate('sortTitle', {type: this.type}),
|
||||
className: 'jsoneditor-sort-asc',
|
||||
click: function () {
|
||||
var anchor = node.editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||
showSortModal(node, anchor)
|
||||
node.showSortModal()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -4455,6 +4454,31 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
|
|||
menu.show(anchor, this.editor.frame);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Show advanced sorting modal
|
||||
*/
|
||||
Node.prototype.showSortModal = function () {
|
||||
var node = this;
|
||||
var container = this.editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||
var paths = node.type === 'array'
|
||||
? node.getChildPaths()
|
||||
: ['.'];
|
||||
|
||||
showSortModal(container, {
|
||||
paths: paths,
|
||||
path: node.sortedBy ? node.sortedBy.path : paths[0],
|
||||
direction: node.sortedBy ? node.sortedBy.direction : 'asc',
|
||||
onSort: function (sortedBy) {
|
||||
var path = sortedBy.path;
|
||||
var pathArray = (path === '.') ? [] : path.split('.').slice(1);
|
||||
|
||||
node.sortedBy = sortedBy
|
||||
node.sort(pathArray, sortedBy.direction)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* get the type of a value
|
||||
* @param {*} value
|
||||
|
|
|
@ -3,11 +3,24 @@ var translate = require('./i18n').translate;
|
|||
|
||||
/**
|
||||
* Show advanced sorting modal
|
||||
* @param {Node} node the node to be sorted
|
||||
* @param {HTMLElement} container The container where to center
|
||||
* the modal and create an overlay
|
||||
* @param {Object} options
|
||||
* Available options:
|
||||
* - {Array} paths The available paths
|
||||
* - {string} path The selected path
|
||||
* - {'asc' | 'desc'} direction The selected direction
|
||||
* - {function} onSort Callback function,
|
||||
* invoked with an object
|
||||
* containing the selected
|
||||
* path and direction
|
||||
*/
|
||||
function showSortModal (node, container) {
|
||||
function showSortModal (container, options) {
|
||||
var paths = options && options.paths || ['.']
|
||||
var selectedPath = options && options.path || paths[0]
|
||||
var selectedDirection = options && options.direction || 'asc'
|
||||
var onSort = options && options.onSort || function () {}
|
||||
|
||||
var content = '<div class="pico-modal-contents">' +
|
||||
'<div class="pico-modal-header">' + translate('sort') + '</div>' +
|
||||
'<form>' +
|
||||
|
@ -61,10 +74,6 @@ function showSortModal (node, container) {
|
|||
var field = modal.modalElem().querySelector('#field');
|
||||
var direction = modal.modalElem().querySelector('#direction');
|
||||
|
||||
var paths = node.type === 'array'
|
||||
? node.getChildPaths()
|
||||
: ['.'];
|
||||
|
||||
paths.forEach(function (path) {
|
||||
var option = document.createElement('option');
|
||||
option.text = path;
|
||||
|
@ -77,8 +86,8 @@ function showSortModal (node, container) {
|
|||
direction.className = 'jsoneditor-button-group jsoneditor-button-group-value-' + direction.value;
|
||||
}
|
||||
|
||||
field.value = node.sortedBy ? node.sortedBy.path : paths[0];
|
||||
setDirection(node.sortedBy ? node.sortedBy.direction : 'asc');
|
||||
field.value = selectedPath || paths[0];
|
||||
setDirection(selectedDirection || 'asc');
|
||||
|
||||
direction.onclick = function (event) {
|
||||
setDirection(event.target.getAttribute('data-value'));
|
||||
|
@ -90,15 +99,10 @@ function showSortModal (node, container) {
|
|||
|
||||
modal.close();
|
||||
|
||||
var path = field.value;
|
||||
var pathArray = (path === '.') ? [] : path.split('.').slice(1);
|
||||
|
||||
node.sortedBy = {
|
||||
path: path,
|
||||
onSort({
|
||||
path: field.value,
|
||||
direction: direction.value
|
||||
};
|
||||
|
||||
node.sort(pathArray, direction.value)
|
||||
})
|
||||
};
|
||||
|
||||
if (form) { // form is not available when JSONEditor is created inside a form
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
var ace = require('./ace');
|
||||
var translate = require('./i18n').translate;
|
||||
var ModeSwitcher = require('./ModeSwitcher');
|
||||
var showSortModal = require('./showSortModal');
|
||||
var showTransformModal = require('./showTransformModal');
|
||||
var util = require('./util');
|
||||
|
||||
// create a mixin with the functions for text mode
|
||||
var textmode = {};
|
||||
|
||||
var DEFAULT_THEME = 'ace/theme/jsoneditor';
|
||||
var DEFAULT_MODAL_ANCHOR = document.body; // TODO: this constant is defined multiple times
|
||||
|
||||
/**
|
||||
* Create a text editor
|
||||
|
@ -47,6 +51,8 @@ textmode.create = function (container, options) {
|
|||
|
||||
// setting default for textmode
|
||||
options.mainMenuBar = options.mainMenuBar !== false;
|
||||
options.enableSort = options.enableSort !== false;
|
||||
options.enableTransform = options.enableTransform !== false;
|
||||
|
||||
this.options = options;
|
||||
|
||||
|
@ -160,6 +166,32 @@ textmode.create = function (container, options) {
|
|||
}
|
||||
};
|
||||
|
||||
// create sort button
|
||||
if (this.options.enableSort) {
|
||||
var sort = document.createElement('button');
|
||||
sort.type = 'button';
|
||||
sort.className = 'jsoneditor-sort';
|
||||
sort.title = translate('sortTitleShort');
|
||||
sort.onclick = function () {
|
||||
me._showSortModal()
|
||||
};
|
||||
this.menu.appendChild(sort);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// // create transform button
|
||||
// if (this.options.enableTransform) {
|
||||
// var transform = document.createElement('button');
|
||||
// transform.type = 'button';
|
||||
// transform.title = translate('transformTitleShort');
|
||||
// transform.className = 'jsoneditor-transform';
|
||||
// transform.onclick = function () {
|
||||
// var anchor = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||
// showTransformModal(me.node, anchor)
|
||||
// };
|
||||
// this.menu.appendChild(transform);
|
||||
// }
|
||||
|
||||
// create repair button
|
||||
var buttonRepair = document.createElement('button');
|
||||
buttonRepair.type = 'button';
|
||||
|
@ -400,6 +432,42 @@ textmode._onChange = function () {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a sort modal
|
||||
* @private
|
||||
*/
|
||||
textmode._showSortModal = function () {
|
||||
var me = this;
|
||||
var container = this.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||
var json = this.get()
|
||||
var paths = Array.isArray(json)
|
||||
? util.getChildPaths(json)
|
||||
: ['.'];
|
||||
|
||||
showSortModal(container, {
|
||||
paths: paths,
|
||||
path: (me.sortedBy && util.contains(paths, me.sortedBy.path))
|
||||
? me.sortedBy.path
|
||||
: paths[0],
|
||||
direction: me.sortedBy ? me.sortedBy.direction : 'asc',
|
||||
onSort: function (sortedBy) {
|
||||
if (Array.isArray(json)) {
|
||||
var sortedJson = util.sort(json, sortedBy.path, sortedBy.direction);
|
||||
|
||||
me.sortedBy = sortedBy
|
||||
me.set(sortedJson);
|
||||
}
|
||||
|
||||
if (util.isObject(json)) {
|
||||
var sortedJson = util.sortObjectKeys(json, sortedBy.direction);
|
||||
|
||||
me.sortedBy = sortedBy;
|
||||
me.set(sortedJson);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle text selection
|
||||
* Calculates the cursor position and selection range and updates menu
|
||||
|
|
|
@ -10,7 +10,6 @@ var Node = require('./Node');
|
|||
var ModeSwitcher = require('./ModeSwitcher');
|
||||
var util = require('./util');
|
||||
var autocomplete = require('./autocomplete');
|
||||
var showSortModal = require('./showSortModal');
|
||||
var showTransformModal = require('./showTransformModal');
|
||||
var translate = require('./i18n').translate;
|
||||
var setLanguages = require('./i18n').setLanguages;
|
||||
|
@ -1022,8 +1021,7 @@ treemode._createFrame = function () {
|
|||
sort.className = 'jsoneditor-sort';
|
||||
sort.title = translate('sortTitleShort');
|
||||
sort.onclick = function () {
|
||||
var anchor = editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||
showSortModal(editor.node, anchor)
|
||||
editor.node.showSortModal()
|
||||
};
|
||||
this.menu.appendChild(sort);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
require('./polyfills');
|
||||
var naturalSort = require('javascript-natural-sort');
|
||||
var jsonlint = require('./assets/jsonlint/jsonlint');
|
||||
var jsonMap = require('json-source-map');
|
||||
var translate = require('./i18n').translate;
|
||||
|
@ -1187,3 +1188,97 @@ exports.findUniqueName = function(name, existingPropNames) {
|
|||
|
||||
return validName
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the child paths of an array
|
||||
* @param {JSON} json
|
||||
* @param {boolean} [includeObjects=false] If true, object and array paths are returned as well
|
||||
* @return {string[]}
|
||||
*/
|
||||
exports.getChildPaths = function (json, includeObjects) {
|
||||
var pathsMap = {};
|
||||
|
||||
function getObjectChildPaths (json, pathsMap, rootPath, includeObjects) {
|
||||
var isValue = !Array.isArray(json) && !exports.isObject(json)
|
||||
|
||||
if (isValue || includeObjects) {
|
||||
pathsMap[rootPath || '.'] = true;
|
||||
}
|
||||
|
||||
if (exports.isObject(json)) {
|
||||
Object.keys(json).forEach(function (field) {
|
||||
getObjectChildPaths(json[field], pathsMap, rootPath + '.' + field, includeObjects);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(json)) {
|
||||
json.forEach(function (item) {
|
||||
getObjectChildPaths(item, pathsMap, '', includeObjects);
|
||||
});
|
||||
}
|
||||
else {
|
||||
pathsMap['.'] = true;
|
||||
}
|
||||
|
||||
return Object.keys(pathsMap).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort object keys using natural sort
|
||||
* @param {Array} array
|
||||
* @param {String} [path] JSON pointer
|
||||
* @param {'asc' | 'desc'} [direction]
|
||||
*/
|
||||
exports.sort = function (array, path, direction) {
|
||||
var parsedPath = path && path !== '.' ? exports.parsePath(path) : []
|
||||
var sign = direction === 'desc' ? -1: 1
|
||||
|
||||
var sortedArray = array.slice()
|
||||
sortedArray.sort(function (a, b) {
|
||||
const aValue = exports.get(a, parsedPath);
|
||||
const bValue = exports.get(b, parsedPath);
|
||||
|
||||
return sign * (aValue > bValue ? 1 : aValue < bValue ? -1 : 0);
|
||||
})
|
||||
|
||||
return sortedArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort object keys using natural sort
|
||||
* @param {Object} object
|
||||
* @param {'asc' | 'desc'} [direction]
|
||||
*/
|
||||
exports.sortObjectKeys = function (object, direction) {
|
||||
var sign = (direction === 'desc') ? -1 : 1;
|
||||
var sortedFields = Object.keys(object).sort(function (a, b) {
|
||||
return sign * naturalSort(a, b);
|
||||
});
|
||||
|
||||
var sortedObject = {};
|
||||
sortedFields.forEach(function (field) {
|
||||
sortedObject[field] = object[field];
|
||||
});
|
||||
|
||||
return sortedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a value is an Object
|
||||
* @param {*} value
|
||||
* @return {boolean}
|
||||
*/
|
||||
exports.isObject = function (value) {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to test whether an array contains an item
|
||||
* @param {Array} array
|
||||
* @param {*} item
|
||||
* @return {boolean} Returns true if `item` is in `array`, returns false otherwise.
|
||||
*/
|
||||
exports.contains = function (array, item) {
|
||||
return array.indexOf(item) !== -1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<!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;
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Switch editor mode using the mode box.
|
||||
Note that the mode can be changed programmatically as well using the method
|
||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<div id="jsoneditor"></div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var container, options, json, editor;
|
||||
|
||||
container = document.getElementById('jsoneditor');
|
||||
|
||||
options = {
|
||||
mode: 'code',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
|
||||
onError: function (err) {
|
||||
alert(err.toString());
|
||||
},
|
||||
onChange: function () {
|
||||
console.log('change');
|
||||
},
|
||||
onChangeJSON: function (json) {
|
||||
console.log('onChangeJSON', json);
|
||||
},
|
||||
onChangeText: function (text) {
|
||||
console.log('onChangeText', text);
|
||||
},
|
||||
indentation: 4,
|
||||
escapeUnicode: true
|
||||
};
|
||||
|
||||
var json = [];
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
var longitude = 4 + i / 10000;
|
||||
var latitude = 51 + i / 10000;
|
||||
|
||||
json.push({
|
||||
name: 'Item ' + i,
|
||||
id: String(i),
|
||||
index: i,
|
||||
time: new Date().toISOString(),
|
||||
location: {
|
||||
latitude: longitude,
|
||||
longitude: latitude,
|
||||
coordinates: [longitude, latitude]
|
||||
},
|
||||
random: Math.random()
|
||||
});
|
||||
}
|
||||
|
||||
editorTest = new JSONEditor(container, options, json);
|
||||
|
||||
// console.log('json', json);
|
||||
console.log('stringified size: ', Math.round(JSON.stringify(json).length / 1024 / 1024), 'MB');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -250,6 +250,109 @@ describe('util', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getChildPaths', function () {
|
||||
it('should extract all child paths of an array containing objects', function () {
|
||||
var json = [
|
||||
{ name: 'A', location: {latitude: 1, longitude: 2} },
|
||||
{ name: 'B', location: {latitude: 1, longitude: 2} },
|
||||
{ name: 'C', timestamp: 0 },
|
||||
];
|
||||
|
||||
assert.deepStrictEqual(util.getChildPaths(json), [
|
||||
'.location.latitude',
|
||||
'.location.longitude',
|
||||
'.name',
|
||||
'.timestamp',
|
||||
])
|
||||
});
|
||||
|
||||
it('should extract all child paths of an array containing objects, including objects', function () {
|
||||
var json = [
|
||||
{ name: 'A', location: {latitude: 1, longitude: 2} },
|
||||
{ name: 'B', location: {latitude: 1, longitude: 2} },
|
||||
{ name: 'C', timestamp: 0 },
|
||||
];
|
||||
|
||||
assert.deepStrictEqual(util.getChildPaths(json, true), [
|
||||
'.',
|
||||
'.location',
|
||||
'.location.latitude',
|
||||
'.location.longitude',
|
||||
'.name',
|
||||
'.timestamp',
|
||||
])
|
||||
});
|
||||
|
||||
it('should extract all child paths of an array containing values', function () {
|
||||
var json = [ 1, 2, 3 ];
|
||||
|
||||
assert.deepStrictEqual(util.getChildPaths(json), [
|
||||
'.'
|
||||
])
|
||||
});
|
||||
|
||||
it('should extract all child paths of a non-array', function () {
|
||||
assert.deepStrictEqual(util.getChildPaths({a: 2, b: {c: 3}}), ['.'])
|
||||
assert.deepStrictEqual(util.getChildPaths('foo'), ['.'])
|
||||
assert.deepStrictEqual(util.getChildPaths(123), ['.'])
|
||||
});
|
||||
})
|
||||
|
||||
it('should test whether something is an object', function () {
|
||||
assert.strictEqual(util.isObject({}), true);
|
||||
assert.strictEqual(util.isObject(new Date()), true);
|
||||
assert.strictEqual(util.isObject([]), false);
|
||||
assert.strictEqual(util.isObject(2), false);
|
||||
assert.strictEqual(util.isObject(null), false);
|
||||
assert.strictEqual(util.isObject(undefined), false);
|
||||
assert.strictEqual(util.isObject(), false);
|
||||
});
|
||||
|
||||
describe('sort', function () {
|
||||
it('should sort an array', function () {
|
||||
var array = [4, 1, 10, 2];
|
||||
assert.deepStrictEqual(util.sort(array), [1, 2, 4, 10]);
|
||||
assert.deepStrictEqual(util.sort(array, '.', 'desc'), [10, 4, 2, 1]);
|
||||
});
|
||||
|
||||
it('should sort an array containing objects', function () {
|
||||
var array = [
|
||||
{ value: 4 },
|
||||
{ value: 1 },
|
||||
{ value: 10 },
|
||||
{ value: 2 }
|
||||
];
|
||||
|
||||
assert.deepStrictEqual(util.sort(array, '.value'), [
|
||||
{ value: 1 },
|
||||
{ value: 2 },
|
||||
{ value: 4 },
|
||||
{ value: 10 }
|
||||
]);
|
||||
|
||||
assert.deepStrictEqual(util.sort(array, '.value', 'desc'), [
|
||||
{ value: 10 },
|
||||
{ value: 4 },
|
||||
{ value: 2 },
|
||||
{ value: 1 }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortObjectKeys', function () {
|
||||
it('should sort the keys of an object', function () {
|
||||
var object = {
|
||||
c: 'c',
|
||||
a: 'a',
|
||||
b: 'b'
|
||||
}
|
||||
assert.strictEqual(JSON.stringify(object), '{"c":"c","a":"a","b":"b"}')
|
||||
assert.strictEqual(JSON.stringify(util.sortObjectKeys(object)), '{"a":"a","b":"b","c":"c"}')
|
||||
assert.strictEqual(JSON.stringify(util.sortObjectKeys(object, 'asc')), '{"a":"a","b":"b","c":"c"}')
|
||||
assert.strictEqual(JSON.stringify(util.sortObjectKeys(object, 'desc')), '{"c":"c","b":"b","a":"a"}')
|
||||
});
|
||||
});
|
||||
|
||||
it('should find a unique name', function () {
|
||||
assert.strictEqual(util.findUniqueName('other', [
|
||||
'a',
|
||||
|
|
Loading…
Reference in New Issue