Merge branch 'feature/custom_query_language' into develop
# Conflicts: # HISTORY.md
This commit is contained in:
commit
c5c64bcde3
|
@ -3,6 +3,14 @@
|
|||
https://github.com/josdejong/jsoneditor
|
||||
|
||||
|
||||
## not yet published, version 8.5.0
|
||||
|
||||
- Implemented support for customizing the query language used in the
|
||||
Transform modal. New options `createQuery`, `executeQuery`, and
|
||||
`queryDescription` are available for this now. An example is available
|
||||
in `examples/23_custom_query_language.html`. See #857, #871.
|
||||
|
||||
|
||||
## 2020-01-25, version 8.4.1
|
||||
|
||||
- Fix `console.log` in production code. Oopsie.
|
||||
|
|
49
docs/api.md
49
docs/api.md
|
@ -589,6 +589,55 @@ Constructs a new JSONEditor.
|
|||
- `{Number} maxVisibleChilds`
|
||||
|
||||
Number of children allowed for a given node before the "show more / show all" message appears (in 'tree', 'view', or 'form' modes). `100` by default.
|
||||
|
||||
- `{ function(json: JSON, queryOptions: QueryOptions) -> string } createQuery`
|
||||
|
||||
Create a query string based on query options filled in the Transform Wizard in the Transform modal.
|
||||
Normally used in combination with `executeQuery`.
|
||||
The input for the function are the entered query options and the current JSON, and the output
|
||||
must be a string containing the query. This query will be executed using `executeQuery`.
|
||||
|
||||
The query options have the following structure:
|
||||
|
||||
```
|
||||
interface QueryOptions {
|
||||
filter?: {
|
||||
field: string | '@'
|
||||
relation: '==' | '!=' | '<' | '<=' | '>' | '>='
|
||||
value: string
|
||||
}
|
||||
sort?: {
|
||||
field: string | '@'
|
||||
direction: 'asc' | 'desc'
|
||||
}
|
||||
projection?: {
|
||||
fields: string[]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that there is a special case `'@'` for `filter.field` and `sort.field`.
|
||||
It means that the field itself is selected, for example when having an array containing numbers.
|
||||
|
||||
A usage example can be found in `examples/23_custom_query_language.html`.
|
||||
|
||||
|
||||
- `{ function(json: JSON, query: string) -> JSON } executeQuery`
|
||||
|
||||
Replace the build-in query language used in the Transform modal with a custom language.
|
||||
Normally used in combination with `createQuery`.
|
||||
The input for the function is the current JSON and a query string, and output must be the transformed JSON.
|
||||
|
||||
A usage example can be found in `examples/23_custom_query_language.html`.
|
||||
|
||||
- `{string} queryDescription`
|
||||
|
||||
A text description displayed on top of the Transform modal.
|
||||
Can be used to explain a custom query language implemented via `createQuery` and `executeQuery`.
|
||||
The text can contain HTML code like a link to a web page.
|
||||
|
||||
A usage example can be found in `examples/23_custom_query_language.html`.
|
||||
|
||||
|
||||
### Methods
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>JSONEditor | Custom query language</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
<script src="https://unpkg.com/lodash@4.17.15/lodash.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
p {
|
||||
max-width: 500px;
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 11pt;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This demo shows how to configure a custom query language.
|
||||
Click on the "Transform" button and try it out.
|
||||
</p>
|
||||
<p>
|
||||
This basic example uses lodash functions <code>filter</code>, <code>sort</code>, and <code>pick</code>,
|
||||
but you can run any JavaScript code.
|
||||
</p>
|
||||
<p class="warning">
|
||||
WARNING: this example uses <code>new Function()</code> which can be dangerous when executed with arbitrary code.
|
||||
Don't use it in production.
|
||||
</p>
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('jsoneditor')
|
||||
const options = {
|
||||
createQuery: function (json, queryOptions) {
|
||||
console.log('createQuery', queryOptions)
|
||||
|
||||
const { filter, sort, projection } = queryOptions
|
||||
let query = 'data'
|
||||
|
||||
if (filter) {
|
||||
// Note that the comparisons embrace type coercion,
|
||||
// so a filter value like '5' (text) will match numbers like 5 too.
|
||||
const getActualValue = filter.field !== '@'
|
||||
? `item => _.get(item, '${filter.field}')`
|
||||
: `item => item`
|
||||
query = `_.filter(${query}, ${getActualValue} ${filter.relation} '${filter.value}')`
|
||||
}
|
||||
|
||||
if (sort) {
|
||||
// The '@' field name is a special case,
|
||||
// which means that the field itself is selected.
|
||||
// For example when we have an array containing numbers.
|
||||
query = sort.field !== '@'
|
||||
? `_.orderBy(${query}, '${sort.field}', '${sort.direction}')`
|
||||
: `_.sortBy(${query}, '${sort.direction}')`
|
||||
}
|
||||
|
||||
if (projection) {
|
||||
// It is possible to make a util function "pickFlat"
|
||||
// and use that when building the query to make it more readable.
|
||||
if (projection.fields.length > 1) {
|
||||
const fields = projection.fields.map(field => {
|
||||
const name = _.last(field.split('.'))
|
||||
return ` '${name}': _.get(item, '${field}')`
|
||||
})
|
||||
query = `_.map(${query}, item => ({\n${fields.join(',\n')}})\n)`
|
||||
} else {
|
||||
const field = projection.fields[0]
|
||||
query = `_.map(${query}, item => _.get(item, '${field}'))`
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
},
|
||||
executeQuery: function (json, query) {
|
||||
console.log('executeQuery', query)
|
||||
|
||||
// WARNING: Using new Function() with arbitrary input can be dangerous! Be careful.
|
||||
const execute = new Function('data', 'return ' + query)
|
||||
|
||||
return execute(json)
|
||||
},
|
||||
queryDescription: 'Enter a JavaScript query to filter, sort, or transform the JSON data.<br/>' +
|
||||
'The <a href="https://lodash.com/" target="_blank">Lodash</a> library is available via <code>_</code> to facilitate this.'
|
||||
}
|
||||
const json = []
|
||||
for (let i = 0; i < 100; i++) {
|
||||
var longitude = 4 + i / 100
|
||||
var latitude = 51 + i / 100
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
const editor = new JSONEditor(container, options, json)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -184,7 +184,8 @@ JSONEditor.VALID_OPTIONS = [
|
|||
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',
|
||||
'sortObjectKeys', 'navigationBar', 'statusBar', 'mainMenuBar', 'languages', 'language', 'enableSort', 'enableTransform',
|
||||
'maxVisibleChilds', 'onValidationError',
|
||||
'modalAnchor', 'popupAnchor'
|
||||
'modalAnchor', 'popupAnchor',
|
||||
'createQuery', 'executeQuery', 'queryDescription'
|
||||
]
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use strict'
|
||||
|
||||
import jmespath from 'jmespath'
|
||||
import naturalSort from 'javascript-natural-sort'
|
||||
import { createAbsoluteAnchor } from './createAbsoluteAnchor'
|
||||
import { ContextMenu } from './ContextMenu'
|
||||
|
@ -3331,7 +3330,7 @@ export class Node {
|
|||
|
||||
// apply the JMESPath query
|
||||
const oldValue = this.getValue()
|
||||
const newValue = jmespath.search(oldValue, query)
|
||||
const newValue = this.editor.options.executeQuery(oldValue, query)
|
||||
this.setValue(newValue)
|
||||
|
||||
const newInternalValue = this.getInternalValue()
|
||||
|
@ -3883,12 +3882,16 @@ export class Node {
|
|||
* Show transform modal
|
||||
*/
|
||||
showTransformModal () {
|
||||
const node = this
|
||||
const { modalAnchor, createQuery, executeQuery, queryDescription } = this.editor.options
|
||||
const json = this.getValue()
|
||||
|
||||
const anchor = this.editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR
|
||||
const json = node.getValue()
|
||||
showTransformModal(anchor, json, query => {
|
||||
node.transform(query)
|
||||
showTransformModal({
|
||||
anchor: modalAnchor || DEFAULT_MODAL_ANCHOR,
|
||||
json,
|
||||
queryDescription, // can be undefined
|
||||
createQuery,
|
||||
executeQuery,
|
||||
onTransform: query => { this.transform(query) }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import jmespath from 'jmespath'
|
||||
import { get, parsePath, parseString } from './util'
|
||||
|
||||
/**
|
||||
* Build a JMESPath query based on query options coming from the wizard
|
||||
* @param {JSON} json The JSON document for which to build the query.
|
||||
* Used for context information like determining
|
||||
* the type of values (string or number)
|
||||
* @param {QueryOptions} queryOptions
|
||||
* @return {string} Returns a query (as string)
|
||||
*/
|
||||
export function createQuery (json, queryOptions) {
|
||||
const { sort, filter, projection } = queryOptions
|
||||
let query = ''
|
||||
|
||||
if (filter) {
|
||||
const examplePath = filter.field !== '@'
|
||||
? ['0'].concat(parsePath('.' + filter.field))
|
||||
: ['0']
|
||||
const exampleValue = get(json, examplePath)
|
||||
const value1 = typeof exampleValue === 'string'
|
||||
? filter.value
|
||||
: parseString(filter.value)
|
||||
|
||||
query += '[? ' +
|
||||
filter.field + ' ' +
|
||||
filter.relation + ' ' +
|
||||
'`' + JSON.stringify(value1) + '`' +
|
||||
']'
|
||||
} else {
|
||||
query += Array.isArray(json)
|
||||
? '[*]'
|
||||
: '@'
|
||||
}
|
||||
|
||||
if (sort) {
|
||||
if (sort.direction === 'desc') {
|
||||
query += ' | reverse(sort_by(@, &' + sort.field + '))'
|
||||
} else {
|
||||
query += ' | sort_by(@, &' + sort.field + ')'
|
||||
}
|
||||
}
|
||||
|
||||
if (projection) {
|
||||
if (query[query.length - 1] !== ']') {
|
||||
query += ' | [*]'
|
||||
}
|
||||
|
||||
if (projection.fields.length === 1) {
|
||||
query += '.' + projection.fields[0]
|
||||
} else if (projection.fields.length > 1) {
|
||||
query += '.{' +
|
||||
projection.fields.map(value => {
|
||||
const parts = value.split('.')
|
||||
const last = parts[parts.length - 1]
|
||||
return last + ': ' + value
|
||||
}).join(', ') +
|
||||
'}'
|
||||
} else { // values.length === 0
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a JMESPath query
|
||||
* @param {JSON} json
|
||||
* @param {string} query
|
||||
* @return {JSON} Returns the transformed JSON
|
||||
*/
|
||||
export function executeQuery (json, query) {
|
||||
return jmespath.search(json, query)
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
'use strict'
|
||||
|
||||
import jmespath from 'jmespath'
|
||||
import { translate } from './i18n'
|
||||
import { ModeSwitcher } from './ModeSwitcher'
|
||||
import { ErrorTable } from './ErrorTable'
|
||||
|
@ -23,6 +22,7 @@ import {
|
|||
sortObjectKeys
|
||||
} from './util'
|
||||
import { History } from './History'
|
||||
import { createQuery, executeQuery } from './jmespathQuery'
|
||||
|
||||
const textmode = textModeMixins[0].mixin
|
||||
|
||||
|
@ -44,6 +44,8 @@ previewmode.create = function (container, options = {}) {
|
|||
options.mainMenuBar = options.mainMenuBar !== false
|
||||
options.enableSort = options.enableSort !== false
|
||||
options.enableTransform = options.enableTransform !== false
|
||||
options.createQuery = options.createQuery || createQuery
|
||||
options.executeQuery = options.executeQuery || executeQuery
|
||||
|
||||
this.options = options
|
||||
|
||||
|
@ -385,18 +387,24 @@ previewmode._showSortModal = function () {
|
|||
* @private
|
||||
*/
|
||||
previewmode._showTransformModal = function () {
|
||||
const me = this
|
||||
|
||||
this.executeWithBusyMessage(() => {
|
||||
const anchor = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR
|
||||
const json = me.get()
|
||||
me._renderPreview() // update array count
|
||||
const { createQuery, executeQuery, modalAnchor, queryDescription } = this.options
|
||||
const json = this.get()
|
||||
|
||||
showTransformModal(anchor, json, query => {
|
||||
me.executeWithBusyMessage(() => {
|
||||
const updatedJson = jmespath.search(json, query)
|
||||
me._setAndFireOnChange(updatedJson)
|
||||
}, 'transforming...')
|
||||
this._renderPreview() // update array count
|
||||
|
||||
showTransformModal({
|
||||
anchor: modalAnchor || DEFAULT_MODAL_ANCHOR,
|
||||
json,
|
||||
queryDescription, // can be undefined
|
||||
createQuery,
|
||||
executeQuery,
|
||||
onTransform: query => {
|
||||
this.executeWithBusyMessage(() => {
|
||||
const updatedJson = executeQuery(json, query)
|
||||
this._setAndFireOnChange(updatedJson)
|
||||
}, 'transforming...')
|
||||
}
|
||||
})
|
||||
}, 'parsing...')
|
||||
}
|
||||
|
|
|
@ -1,28 +1,43 @@
|
|||
import jmespath from 'jmespath'
|
||||
import picoModal from 'picomodal'
|
||||
import Selectr from './assets/selectr/selectr'
|
||||
import { translate } from './i18n'
|
||||
import { stringifyPartial } from './jsonUtils'
|
||||
import { getChildPaths, get, parsePath, parseString, debounce } from './util'
|
||||
import { getChildPaths, debounce } from './util'
|
||||
import { MAX_PREVIEW_CHARACTERS } from './constants'
|
||||
|
||||
const DEFAULT_DESCRIPTION =
|
||||
'Enter a <a href="http://jmespath.org" target="_blank">JMESPath</a> query to filter, sort, or transform the JSON data.<br/>' +
|
||||
'To learn JMESPath, go to <a href="http://jmespath.org/tutorial.html" target="_blank">the interactive tutorial</a>.'
|
||||
|
||||
/**
|
||||
* Show advanced filter and transform modal using JMESPath
|
||||
* @param {HTMLElement} container The container where to center
|
||||
* the modal and create an overlay
|
||||
* @param {JSON} json The json data to be transformed
|
||||
* @param {function} onTransform Callback invoked with the created
|
||||
* query as callback
|
||||
* @param {Object} params
|
||||
* @property {HTMLElement} container The container where to center
|
||||
* the modal and create an overlay
|
||||
* @property {JSON} json The json data to be transformed
|
||||
* @property {string} [queryDescription] Optional custom description explaining
|
||||
* the transform functionality
|
||||
* @property {function} createQuery Function called to create a query
|
||||
* from the wizard form
|
||||
* @property {function} executeQuery Execute a query for the preview pane
|
||||
* @property {function} onTransform Callback invoked with the created
|
||||
* query as callback
|
||||
*/
|
||||
export function showTransformModal (container, json, onTransform) {
|
||||
export function showTransformModal (
|
||||
{
|
||||
container,
|
||||
json,
|
||||
queryDescription = DEFAULT_DESCRIPTION,
|
||||
createQuery,
|
||||
executeQuery,
|
||||
onTransform
|
||||
}
|
||||
) {
|
||||
const value = json
|
||||
|
||||
const content = '<label class="pico-modal-contents">' +
|
||||
'<div class="pico-modal-header">' + translate('transform') + '</div>' +
|
||||
'<p>' +
|
||||
'Enter a <a href="http://jmespath.org" target="_blank">JMESPath</a> query to filter, sort, or transform the JSON data.<br/>' +
|
||||
'To learn JMESPath, go to <a href="http://jmespath.org/tutorial.html" target="_blank">the interactive tutorial</a>.' +
|
||||
'</p>' +
|
||||
'<p>' + queryDescription + '</p>' +
|
||||
'<div class="jsoneditor-jmespath-label">' + translate('transformWizardLabel') + ' </div>' +
|
||||
'<div id="wizard" class="jsoneditor-jmespath-block jsoneditor-jmespath-wizard">' +
|
||||
' <table class="jsoneditor-jmespath-wizard-table">' +
|
||||
|
@ -176,8 +191,6 @@ export function showTransformModal (container, json, onTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
query.value = Array.isArray(value) ? '[*]' : '@'
|
||||
|
||||
function preprocessPath (path) {
|
||||
return (path === '')
|
||||
? '@'
|
||||
|
@ -186,69 +199,9 @@ export function showTransformModal (container, json, onTransform) {
|
|||
: path
|
||||
}
|
||||
|
||||
function generateQueryFromWizard () {
|
||||
if (filterField.value && filterRelation.value && filterValue.value) {
|
||||
const field1 = filterField.value
|
||||
const examplePath = field1 !== '@'
|
||||
? ['0'].concat(parsePath('.' + field1))
|
||||
: ['0']
|
||||
const exampleValue = get(value, examplePath)
|
||||
const value1 = typeof exampleValue === 'string'
|
||||
? filterValue.value
|
||||
: parseString(filterValue.value)
|
||||
|
||||
query.value = '[? ' +
|
||||
field1 + ' ' +
|
||||
filterRelation.value + ' ' +
|
||||
'`' + JSON.stringify(value1) + '`' +
|
||||
']'
|
||||
} else {
|
||||
query.value = '[*]'
|
||||
}
|
||||
|
||||
if (sortField.value && sortOrder.value) {
|
||||
const field2 = sortField.value
|
||||
if (sortOrder.value === 'desc') {
|
||||
query.value += ' | reverse(sort_by(@, &' + field2 + '))'
|
||||
} else {
|
||||
query.value += ' | sort_by(@, &' + field2 + ')'
|
||||
}
|
||||
}
|
||||
|
||||
if (selectFields.value) {
|
||||
const values = []
|
||||
for (let i = 0; i < selectFields.options.length; i++) {
|
||||
if (selectFields.options[i].selected) {
|
||||
const selectedValue = selectFields.options[i].value
|
||||
values.push(selectedValue)
|
||||
}
|
||||
}
|
||||
|
||||
if (query.value[query.value.length - 1] !== ']') {
|
||||
query.value += ' | [*]'
|
||||
}
|
||||
|
||||
if (values.length === 1) {
|
||||
query.value += '.' + values[0]
|
||||
} else if (values.length > 1) {
|
||||
query.value += '.{' +
|
||||
values.map(value => {
|
||||
const parts = value.split('.')
|
||||
const last = parts[parts.length - 1]
|
||||
return last + ': ' + value
|
||||
}).join(', ') +
|
||||
'}'
|
||||
} else { // values.length === 0
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
debouncedUpdatePreview()
|
||||
}
|
||||
|
||||
function updatePreview () {
|
||||
try {
|
||||
const transformed = jmespath.search(value, query.value)
|
||||
const transformed = executeQuery(value, query.value)
|
||||
|
||||
preview.className = 'jsoneditor-transform-preview'
|
||||
preview.value = stringifyPartial(transformed, 2, MAX_PREVIEW_CHARACTERS)
|
||||
|
@ -261,10 +214,61 @@ export function showTransformModal (container, json, onTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
var debouncedUpdatePreview = debounce(updatePreview, 300)
|
||||
const debouncedUpdatePreview = debounce(updatePreview, 300)
|
||||
|
||||
function tryCreateQuery (json, queryOptions) {
|
||||
try {
|
||||
query.value = createQuery(json, queryOptions)
|
||||
ok.disabled = false
|
||||
|
||||
debouncedUpdatePreview()
|
||||
} catch (err) {
|
||||
const message = 'Error: an error happened when executing "createQuery": ' + (err.message || err.toString())
|
||||
|
||||
query.value = ''
|
||||
ok.disabled = true
|
||||
|
||||
preview.className = 'jsoneditor-transform-preview jsoneditor-error'
|
||||
preview.value = message
|
||||
}
|
||||
}
|
||||
|
||||
function generateQueryFromWizard () {
|
||||
const queryOptions = {}
|
||||
|
||||
if (filterField.value && filterRelation.value && filterValue.value) {
|
||||
queryOptions.filter = {
|
||||
field: filterField.value,
|
||||
relation: filterRelation.value,
|
||||
value: filterValue.value
|
||||
}
|
||||
}
|
||||
|
||||
if (sortField.value && sortOrder.value) {
|
||||
queryOptions.sort = {
|
||||
field: sortField.value,
|
||||
direction: sortOrder.value
|
||||
}
|
||||
}
|
||||
|
||||
if (selectFields.value) {
|
||||
const fields = []
|
||||
for (let i = 0; i < selectFields.options.length; i++) {
|
||||
if (selectFields.options[i].selected) {
|
||||
const selectedField = selectFields.options[i].value
|
||||
fields.push(selectedField)
|
||||
}
|
||||
}
|
||||
|
||||
queryOptions.projection = {
|
||||
fields
|
||||
}
|
||||
}
|
||||
|
||||
tryCreateQuery(json, queryOptions)
|
||||
}
|
||||
|
||||
query.oninput = debouncedUpdatePreview
|
||||
debouncedUpdatePreview()
|
||||
|
||||
ok.onclick = event => {
|
||||
event.preventDefault()
|
||||
|
@ -275,6 +279,9 @@ export function showTransformModal (container, json, onTransform) {
|
|||
onTransform(query.value)
|
||||
}
|
||||
|
||||
// initialize with empty query
|
||||
tryCreateQuery(json, {})
|
||||
|
||||
setTimeout(() => {
|
||||
query.select()
|
||||
query.focus()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
import ace from './ace'
|
||||
import jmespath from 'jmespath'
|
||||
import { translate } from './i18n'
|
||||
import { ModeSwitcher } from './ModeSwitcher'
|
||||
import { ErrorTable } from './ErrorTable'
|
||||
|
@ -26,6 +25,7 @@ import {
|
|||
} from './util'
|
||||
import { DEFAULT_MODAL_ANCHOR } from './constants'
|
||||
import { tryRequireThemeJsonEditor } from './tryRequireThemeJsonEditor'
|
||||
import { createQuery, executeQuery } from './jmespathQuery'
|
||||
|
||||
// create a mixin with the functions for text mode
|
||||
const textmode = {}
|
||||
|
@ -47,6 +47,8 @@ textmode.create = function (container, options = {}) {
|
|||
options.mainMenuBar = options.mainMenuBar !== false
|
||||
options.enableSort = options.enableSort !== false
|
||||
options.enableTransform = options.enableTransform !== false
|
||||
options.createQuery = options.createQuery || createQuery
|
||||
options.executeQuery = options.executeQuery || executeQuery
|
||||
|
||||
this.options = options
|
||||
|
||||
|
@ -425,12 +427,19 @@ textmode._showSortModal = function () {
|
|||
* @private
|
||||
*/
|
||||
textmode._showTransformModal = function () {
|
||||
const me = this
|
||||
const anchor = this.options.modalAnchor || DEFAULT_MODAL_ANCHOR
|
||||
const { modalAnchor, createQuery, executeQuery, queryDescription } = this.options
|
||||
const json = this.get()
|
||||
showTransformModal(anchor, json, query => {
|
||||
const updatedJson = jmespath.search(json, query)
|
||||
me.set(updatedJson)
|
||||
|
||||
showTransformModal({
|
||||
anchor: modalAnchor || DEFAULT_MODAL_ANCHOR,
|
||||
json,
|
||||
queryDescription, // can be undefined
|
||||
createQuery,
|
||||
executeQuery,
|
||||
onTransform: query => {
|
||||
const updatedJson = executeQuery(json, query)
|
||||
this.set(updatedJson)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
} from './util'
|
||||
import { autocomplete } from './autocomplete'
|
||||
import { setLanguage, setLanguages, translate } from './i18n'
|
||||
import { createQuery, executeQuery } from './jmespathQuery'
|
||||
|
||||
// create a mixin with the functions for tree mode
|
||||
const treemode = {}
|
||||
|
@ -156,6 +157,8 @@ treemode._setOptions = function (options) {
|
|||
},
|
||||
timestampTag: true,
|
||||
timestampFormat: null,
|
||||
createQuery,
|
||||
executeQuery,
|
||||
onEvent: null,
|
||||
enableSort: true,
|
||||
enableTransform: true
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
/**
|
||||
* @typedef {object} QueryOptions
|
||||
* @property {FilterOptions} [filter]
|
||||
* @property {SortOptions} [sort]
|
||||
* @property {ProjectionOptions} [projection]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FilterOptions
|
||||
* @property {string} field
|
||||
* @property {string} relation Can be '==', '<', etc
|
||||
* @property {string} value
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SortOptions
|
||||
* @property {string} field
|
||||
* @property {string} direction Can be 'asc' or 'desc'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ProjectionOptions
|
||||
* @property {string[]} fields
|
||||
*/
|
Loading…
Reference in New Issue