Skip to content

Commit

Permalink
Implemented sort button in code/text mode
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Jun 19, 2019
1 parent 12dc040 commit 74b816a
Show file tree
Hide file tree
Showing 7 changed files with 402 additions and 21 deletions.
28 changes: 26 additions & 2 deletions src/js/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
});
}
Expand Down Expand Up @@ -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
Expand Down
36 changes: 20 additions & 16 deletions src/js/showSortModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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>' +
Expand Down Expand Up @@ -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;
Expand All @@ -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'));
Expand All @@ -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
Expand Down
68 changes: 68 additions & 0 deletions src/js/textmode.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions src/js/treemode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
95 changes: 95 additions & 0 deletions src/js/util.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
Loading

0 comments on commit 74b816a

Please sign in to comment.