Many patch action working again in UI
This commit is contained in:
parent
156f330e4e
commit
1a6661fbb5
|
@ -1,9 +1,11 @@
|
||||||
import last from 'lodash/last'
|
import last from 'lodash/last'
|
||||||
import initial from 'lodash/initial'
|
import initial from 'lodash/initial'
|
||||||
import {
|
import {
|
||||||
compileJSONPointer, getInEson, esonToJson, findNextProp,
|
META,
|
||||||
|
compileJSONPointer, esonToJson, findNextProp,
|
||||||
pathsFromSelection, findRootPath, findSelectionIndices
|
pathsFromSelection, findRootPath, findSelectionIndices
|
||||||
} from './eson'
|
} from './eson'
|
||||||
|
import { cloneWithSymbols, getIn, setIn } from './utils/immutabilityHelpers'
|
||||||
import { findUniqueName } from './utils/stringUtils'
|
import { findUniqueName } from './utils/stringUtils'
|
||||||
import { isObject, stringConvert } from './utils/typeUtils'
|
import { isObject, stringConvert } from './utils/typeUtils'
|
||||||
import { compareAsc, compareDesc, strictShallowEqual } from './utils/arrayUtils'
|
import { compareAsc, compareDesc, strictShallowEqual } from './utils/arrayUtils'
|
||||||
|
@ -11,14 +13,14 @@ import { compareAsc, compareDesc, strictShallowEqual } from './utils/arrayUtils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a JSONPatch to change the value of a property or item
|
* Create a JSONPatch to change the value of a property or item
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function changeValue (data, path, value) {
|
export function changeValue (eson, path, value) {
|
||||||
// console.log('changeValue', data, value)
|
// console.log('changeValue', data, value)
|
||||||
const oldDataValue = getInEson(data, path)
|
const oldDataValue = getIn(eson, path)
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'replace',
|
op: 'replace',
|
||||||
|
@ -32,18 +34,18 @@ export function changeValue (data, path, value) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a JSONPatch to change a property name
|
* Create a JSONPatch to change a property name
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Path} parentPath
|
* @param {Path} parentPath
|
||||||
* @param {string} oldProp
|
* @param {string} oldProp
|
||||||
* @param {string} newProp
|
* @param {string} newProp
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function changeProperty (data, parentPath, oldProp, newProp) {
|
export function changeProperty (eson, parentPath, oldProp, newProp) {
|
||||||
// console.log('changeProperty', parentPath, oldProp, newProp)
|
// console.log('changeProperty', parentPath, oldProp, newProp)
|
||||||
const parent = getInEson(data, parentPath)
|
const parent = getIn(eson, parentPath)
|
||||||
|
|
||||||
// prevent duplicate property names
|
// prevent duplicate property names
|
||||||
const uniqueNewProp = findUniqueName(newProp, parent.props.map(p => p.name))
|
const uniqueNewProp = findUniqueName(newProp, parent[META].keys)
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'move',
|
op: 'move',
|
||||||
|
@ -57,13 +59,13 @@ export function changeProperty (data, parentPath, oldProp, newProp) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a JSONPatch to change the type of a property or item
|
* Create a JSONPatch to change the type of a property or item
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @param {ESONType} type
|
* @param {ESONType} type
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function changeType (data, path, type) {
|
export function changeType (eson, path, type) {
|
||||||
const oldValue = esonToJson(getInEson(data, path))
|
const oldValue = esonToJson(getIn(eson, path))
|
||||||
const newValue = convertType(oldValue, type)
|
const newValue = convertType(oldValue, type)
|
||||||
|
|
||||||
// console.log('changeType', path, type, oldValue, newValue)
|
// console.log('changeType', path, type, oldValue, newValue)
|
||||||
|
@ -85,20 +87,20 @@ export function changeType (data, path, type) {
|
||||||
* a unique property name for the duplicated node in case of duplicating
|
* a unique property name for the duplicated node in case of duplicating
|
||||||
* and object property
|
* and object property
|
||||||
*
|
*
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Selection} selection
|
* @param {Selection} selection
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function duplicate (data, selection) {
|
export function duplicate (eson, selection) {
|
||||||
// console.log('duplicate', path)
|
// console.log('duplicate', path)
|
||||||
if (!selection.start || !selection.end) {
|
if (!selection.start || !selection.end) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const root = getInEson(data, rootPath)
|
const root = getIn(eson, rootPath)
|
||||||
const { maxIndex } = findSelectionIndices(root, rootPath, selection)
|
const { maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
const paths = pathsFromSelection(data, selection)
|
const paths = pathsFromSelection(eson, selection)
|
||||||
|
|
||||||
if (root.type === 'Array') {
|
if (root.type === 'Array') {
|
||||||
return paths.map((path, offset) => ({
|
return paths.map((path, offset) => ({
|
||||||
|
@ -108,12 +110,11 @@ export function duplicate (data, selection) {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // object.type === 'Object'
|
||||||
const nextProp = root.props && root.props[maxIndex]
|
const before = root[META].keys[maxIndex] || null
|
||||||
const before = nextProp ? nextProp.name : null
|
|
||||||
|
|
||||||
return paths.map(path => {
|
return paths.map(path => {
|
||||||
const prop = last(path)
|
const prop = last(path)
|
||||||
const newProp = findUniqueName(prop, root.props.map(p => p.name))
|
const newProp = findUniqueName(prop, root[META].keys)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
op: 'copy',
|
op: 'copy',
|
||||||
|
@ -134,14 +135,14 @@ export function duplicate (data, selection) {
|
||||||
* a unique property name for the inserted node in case of duplicating
|
* a unique property name for the inserted node in case of duplicating
|
||||||
* and object property
|
* and object property
|
||||||
*
|
*
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values
|
* @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function insertBefore (data, path, values) { // TODO: find a better name and define datastructure for values
|
export function insertBefore (eson, path, values) { // TODO: find a better name and define datastructure for values
|
||||||
const parentPath = initial(path)
|
const parentPath = initial(path)
|
||||||
const parent = getInEson(data, parentPath)
|
const parent = getIn(eson, parentPath)
|
||||||
|
|
||||||
if (parent.type === 'Array') {
|
if (parent.type === 'Array') {
|
||||||
const startIndex = parseInt(last(path))
|
const startIndex = parseInt(last(path))
|
||||||
|
@ -157,7 +158,7 @@ export function insertBefore (data, path, values) { // TODO: find a better name
|
||||||
else { // object.type === 'Object'
|
else { // object.type === 'Object'
|
||||||
const before = last(path)
|
const before = last(path)
|
||||||
return values.map(entry => {
|
return values.map(entry => {
|
||||||
const newProp = findUniqueName(entry.name, parent.props.map(p => p.name))
|
const newProp = findUniqueName(entry.name, parent[META].keys)
|
||||||
return {
|
return {
|
||||||
op: 'add',
|
op: 'add',
|
||||||
path: compileJSONPointer(parentPath.concat(newProp)),
|
path: compileJSONPointer(parentPath.concat(newProp)),
|
||||||
|
@ -178,18 +179,18 @@ export function insertBefore (data, path, values) { // TODO: find a better name
|
||||||
* a unique property name for the inserted node in case of duplicating
|
* a unique property name for the inserted node in case of duplicating
|
||||||
* and object property
|
* and object property
|
||||||
*
|
*
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Selection} selection
|
* @param {Selection} selection
|
||||||
* @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values
|
* @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function replace (data, selection, values) { // TODO: find a better name and define datastructure for values
|
export function replace (eson, selection, values) { // TODO: find a better name and define datastructure for values
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const root = getInEson(data, rootPath)
|
const root = getIn(eson, rootPath)
|
||||||
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
|
|
||||||
if (root.type === 'Array') {
|
if (root.type === 'Array') {
|
||||||
const removeActions = removeAll(pathsFromSelection(data, selection))
|
const removeActions = removeAll(pathsFromSelection(eson, selection))
|
||||||
const insertActions = values.map((entry, offset) => ({
|
const insertActions = values.map((entry, offset) => ({
|
||||||
op: 'add',
|
op: 'add',
|
||||||
path: compileJSONPointer(rootPath.concat(minIndex + offset)),
|
path: compileJSONPointer(rootPath.concat(minIndex + offset)),
|
||||||
|
@ -202,12 +203,11 @@ export function replace (data, selection, values) { // TODO: find a better name
|
||||||
return removeActions.concat(insertActions)
|
return removeActions.concat(insertActions)
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // object.type === 'Object'
|
||||||
const nextProp = root.props && root.props[maxIndex]
|
const before = root[META].keys[maxIndex] || null
|
||||||
const before = nextProp ? nextProp.name : null
|
|
||||||
|
|
||||||
const removeActions = removeAll(pathsFromSelection(data, selection))
|
const removeActions = removeAll(pathsFromSelection(eson, selection))
|
||||||
const insertActions = values.map(entry => {
|
const insertActions = values.map(entry => {
|
||||||
const newProp = findUniqueName(entry.name, root.props.map(p => p.name))
|
const newProp = findUniqueName(entry.name, root[META].keys)
|
||||||
return {
|
return {
|
||||||
op: 'add',
|
op: 'add',
|
||||||
path: compileJSONPointer(rootPath.concat(newProp)),
|
path: compileJSONPointer(rootPath.concat(newProp)),
|
||||||
|
@ -230,15 +230,15 @@ export function replace (data, selection, values) { // TODO: find a better name
|
||||||
* a unique property name for the inserted node in case of duplicating
|
* a unique property name for the inserted node in case of duplicating
|
||||||
* and object property
|
* and object property
|
||||||
*
|
*
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Path} parentPath
|
* @param {Path} parentPath
|
||||||
* @param {ESONType} type
|
* @param {ESONType} type
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function append (data, parentPath, type) {
|
export function append (eson, parentPath, type) {
|
||||||
// console.log('append', parentPath, value)
|
// console.log('append', parentPath, value)
|
||||||
|
|
||||||
const parent = getInEson(data, parentPath)
|
const parent = getIn(eson, parentPath)
|
||||||
const value = createEntry(type)
|
const value = createEntry(type)
|
||||||
|
|
||||||
if (parent.type === 'Array') {
|
if (parent.type === 'Array') {
|
||||||
|
@ -252,7 +252,7 @@ export function append (data, parentPath, type) {
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // object.type === 'Object'
|
||||||
const newProp = findUniqueName('', parent.props.map(p => p.name))
|
const newProp = findUniqueName('', parent[META].keys)
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'add',
|
op: 'add',
|
||||||
|
@ -295,50 +295,49 @@ export function removeAll (paths) {
|
||||||
/**
|
/**
|
||||||
* Create a JSONPatch to order the items of an array or the properties of an object in ascending
|
* Create a JSONPatch to order the items of an array or the properties of an object in ascending
|
||||||
* or descending order
|
* or descending order
|
||||||
* @param {ESON} data
|
* @param {ESON} eson
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @param {'asc' | 'desc' | null} [order=null] If not provided, will toggle current ordering
|
* @param {'asc' | 'desc' | null} [order=null] If not provided, will toggle current ordering
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function sort (data, path, order = null) {
|
export function sort (eson, path, order = null) {
|
||||||
// console.log('sort', path, order)
|
// console.log('sort', path, order)
|
||||||
|
|
||||||
const compare = order === 'desc' ? compareDesc : compareAsc
|
const compare = order === 'desc' ? compareDesc : compareAsc
|
||||||
const object = getInEson(data, path)
|
const object = getIn(eson, path)
|
||||||
|
|
||||||
if (object.type === 'Array') {
|
if (object.type === 'Array') {
|
||||||
const orderedItems = object.items.slice(0)
|
const orderedItems = object.slice()
|
||||||
|
|
||||||
// order the items by value
|
// order the items by value
|
||||||
orderedItems.sort((a, b) => compare(a.value, b.value))
|
orderedItems.sort((a, b) => compare(a.value, b.value))
|
||||||
|
|
||||||
// when no order is provided, test whether ordering ascending
|
// when no order is provided, test whether ordering ascending
|
||||||
// changed anything. If not, sort descending
|
// changed anything. If not, sort descending
|
||||||
if (!order && strictShallowEqual(object.items, orderedItems)) {
|
if (!order && strictShallowEqual(object, orderedItems)) {
|
||||||
orderedItems.reverse()
|
orderedItems.reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'replace',
|
op: 'replace',
|
||||||
path: compileJSONPointer(path),
|
path: compileJSONPointer(path),
|
||||||
value: esonToJson({
|
value: orderedItems
|
||||||
type: 'Array',
|
|
||||||
items: orderedItems
|
|
||||||
})
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // object.type === 'Object'
|
||||||
const orderedProps = object.props.slice(0)
|
|
||||||
|
|
||||||
// order the properties by key
|
// order the properties by key
|
||||||
orderedProps.sort((a, b) => compare(a.name, b.name))
|
const orderedKeys = object[META].keys.slice().sort((a, b) => compare(a.name, b.name))
|
||||||
|
|
||||||
// when no order is provided, test whether ordering ascending
|
// when no order is provided, test whether ordering ascending
|
||||||
// changed anything. If not, sort descending
|
// changed anything. If not, sort descending
|
||||||
if (!order && strictShallowEqual(object.props, orderedProps)) {
|
if (!order && strictShallowEqual(object[META].keys, orderedKeys)) {
|
||||||
orderedProps.reverse()
|
orderedKeys.reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const orderedProps = cloneWithSymbols(object)
|
||||||
|
orderedProps[META] = setIn(object[META], ['keys'], orderedKeys)
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'replace',
|
op: 'replace',
|
||||||
path: compileJSONPointer(path),
|
path: compileJSONPointer(path),
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Ajv from 'ajv'
|
||||||
import { parseJSON } from '../utils/jsonUtils'
|
import { parseJSON } from '../utils/jsonUtils'
|
||||||
import { escapeUnicodeChars } from '../utils/stringUtils'
|
import { escapeUnicodeChars } from '../utils/stringUtils'
|
||||||
import { enrichSchemaError, limitErrors } from '../utils/schemaUtils'
|
import { enrichSchemaError, limitErrors } from '../utils/schemaUtils'
|
||||||
import { jsonToEsonOld, esonToJson } from '../eson'
|
import { jsonToEson, esonToJson } from '../eson'
|
||||||
import { patchEson } from '../patchEson'
|
import { patchEson } from '../patchEson'
|
||||||
import { createFindKeyBinding } from '../utils/keyBindings'
|
import { createFindKeyBinding } from '../utils/keyBindings'
|
||||||
import { KEY_BINDINGS } from '../constants'
|
import { KEY_BINDINGS } from '../constants'
|
||||||
|
@ -334,7 +334,7 @@ export default class TextMode extends Component {
|
||||||
patch (actions) {
|
patch (actions) {
|
||||||
const json = this.get()
|
const json = this.get()
|
||||||
|
|
||||||
const data = jsonToEsonOld(json)
|
const data = jsonToEson(json)
|
||||||
const result = patchEson(data, actions)
|
const result = patchEson(data, actions)
|
||||||
|
|
||||||
this.set(esonToJson(result.data))
|
this.set(esonToJson(result.data))
|
||||||
|
|
|
@ -336,19 +336,19 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangeValue = (path, value) => {
|
handleChangeValue = (path, value) => {
|
||||||
this.handlePatch(changeValue(this.state.data, path, value))
|
this.handlePatch(changeValue(this.state.eson, path, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangeProperty = (parentPath, oldProp, newProp) => {
|
handleChangeProperty = (parentPath, oldProp, newProp) => {
|
||||||
this.handlePatch(changeProperty(this.state.data, parentPath, oldProp, newProp))
|
this.handlePatch(changeProperty(this.state.eson, parentPath, oldProp, newProp))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangeType = (path, type) => {
|
handleChangeType = (path, type) => {
|
||||||
this.handlePatch(changeType(this.state.data, path, type))
|
this.handlePatch(changeType(this.state.eson, path, type))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInsert = (path, type) => {
|
handleInsert = (path, type) => {
|
||||||
this.handlePatch(insertBefore(this.state.data, path, [{
|
this.handlePatch(insertBefore(this.state.eson, path, [{
|
||||||
type,
|
type,
|
||||||
name: '',
|
name: '',
|
||||||
value: createEntry(type)
|
value: createEntry(type)
|
||||||
|
@ -367,7 +367,7 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAppend = (parentPath, type) => {
|
handleAppend = (parentPath, type) => {
|
||||||
this.handlePatch(append(this.state.data, parentPath, type))
|
this.handlePatch(append(this.state.eson, parentPath, type))
|
||||||
|
|
||||||
// apply focus to new node
|
// apply focus to new node
|
||||||
this.focusToNext(parentPath)
|
this.focusToNext(parentPath)
|
||||||
|
@ -375,7 +375,7 @@ export default class TreeMode extends Component {
|
||||||
|
|
||||||
handleDuplicate = () => {
|
handleDuplicate = () => {
|
||||||
if (this.state.selection) {
|
if (this.state.selection) {
|
||||||
this.handlePatch(duplicate(this.state.data, this.state.selection))
|
this.handlePatch(duplicate(this.state.eson, this.state.selection))
|
||||||
// TODO: focus to duplicated selection
|
// TODO: focus to duplicated selection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,7 @@ export default class TreeMode extends Component {
|
||||||
else if (this.state.selection) {
|
else if (this.state.selection) {
|
||||||
// remove selection
|
// remove selection
|
||||||
// TODO: select next property? (same as when removing a path?)
|
// TODO: select next property? (same as when removing a path?)
|
||||||
const paths = pathsFromSelection(this.state.data, this.state.selection)
|
const paths = pathsFromSelection(this.state.eson, this.state.selection)
|
||||||
this.setState({ selection: null })
|
this.setState({ selection: null })
|
||||||
this.handlePatch(removeAll(paths))
|
this.handlePatch(removeAll(paths))
|
||||||
}
|
}
|
||||||
|
@ -446,13 +446,13 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDownPaste = (event) => {
|
handleKeyDownPaste = (event) => {
|
||||||
const { clipboard, data } = this.state
|
const { clipboard, eson } = this.state
|
||||||
|
|
||||||
if (clipboard && clipboard.length > 0) {
|
if (clipboard && clipboard.length > 0) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
const path = this.findDataPathFromElement(event.target)
|
const path = this.findDataPathFromElement(event.target)
|
||||||
this.handlePatch(insertBefore(data, path, clipboard))
|
this.handlePatch(insertBefore(eson, path, clipboard))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +460,7 @@ export default class TreeMode extends Component {
|
||||||
const path = this.findDataPathFromElement(event.target)
|
const path = this.findDataPathFromElement(event.target)
|
||||||
if (path) {
|
if (path) {
|
||||||
const selection = { start: path, end: path }
|
const selection = { start: path, end: path }
|
||||||
this.handlePatch(duplicate(this.state.data, selection))
|
this.handlePatch(duplicate(this.state.eson, selection))
|
||||||
|
|
||||||
// apply focus to the duplicated node
|
// apply focus to the duplicated node
|
||||||
this.focusToNext(path)
|
this.focusToNext(path)
|
||||||
|
@ -470,9 +470,9 @@ export default class TreeMode extends Component {
|
||||||
handleCut = () => {
|
handleCut = () => {
|
||||||
const selection = this.state.selection
|
const selection = this.state.selection
|
||||||
if (selection && selection.start && selection.end) {
|
if (selection && selection.start && selection.end) {
|
||||||
const data = this.state.data
|
const eson = this.state.eson
|
||||||
const paths = pathsFromSelection(data, selection)
|
const paths = pathsFromSelection(eson, selection)
|
||||||
const clipboard = contentsFromPaths(data, paths)
|
const clipboard = contentsFromPaths(eson, paths)
|
||||||
|
|
||||||
this.setState({ clipboard, selection: null })
|
this.setState({ clipboard, selection: null })
|
||||||
|
|
||||||
|
@ -490,9 +490,9 @@ export default class TreeMode extends Component {
|
||||||
handleCopy = () => {
|
handleCopy = () => {
|
||||||
const selection = this.state.selection
|
const selection = this.state.selection
|
||||||
if (selection && selection.start && selection.end) {
|
if (selection && selection.start && selection.end) {
|
||||||
const data = this.state.data
|
const eson = this.state.eson
|
||||||
const paths = pathsFromSelection(data, selection)
|
const paths = pathsFromSelection(eson, selection)
|
||||||
const clipboard = contentsFromPaths(data, paths)
|
const clipboard = contentsFromPaths(eson, paths)
|
||||||
|
|
||||||
this.setState({ clipboard })
|
this.setState({ clipboard })
|
||||||
}
|
}
|
||||||
|
@ -503,11 +503,11 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePaste = () => {
|
handlePaste = () => {
|
||||||
const { data, selection, clipboard } = this.state
|
const { eson, selection, clipboard } = this.state
|
||||||
|
|
||||||
if (selection && clipboard && clipboard.length > 0) {
|
if (selection && clipboard && clipboard.length > 0) {
|
||||||
this.setState({ selection: null })
|
this.setState({ selection: null })
|
||||||
this.handlePatch(replace(data, selection, clipboard))
|
this.handlePatch(replace(eson, selection, clipboard))
|
||||||
// TODO: select the pasted contents
|
// TODO: select the pasted contents
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,7 +539,7 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSort = (path, order = null) => {
|
handleSort = (path, order = null) => {
|
||||||
this.handlePatch(sort(this.state.data, path, order))
|
this.handlePatch(sort(this.state.eson, path, order))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelect = (selection: Selection) => {
|
handleSelect = (selection: Selection) => {
|
||||||
|
@ -753,13 +753,13 @@ export default class TreeMode extends Component {
|
||||||
* Emit an onChange event when there is a listener for it.
|
* Emit an onChange event when there is a listener for it.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
emitOnChange (patch: ESONPatch, revert: ESONPatch, data: ESON) {
|
emitOnChange (patch: ESONPatch, revert: ESONPatch, eson: ESON) {
|
||||||
if (this.props.onPatch) {
|
if (this.props.onPatch) {
|
||||||
this.props.onPatch(patch, revert)
|
this.props.onPatch(patch, revert)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.onChange || this.props.onChangeText) {
|
if (this.props.onChange || this.props.onChangeText) {
|
||||||
const json = esonToJson(data)
|
const json = esonToJson(eson)
|
||||||
|
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
this.props.onChange(json)
|
this.props.onChange(json)
|
||||||
|
@ -798,10 +798,10 @@ export default class TreeMode extends Component {
|
||||||
const historyIndex = this.state.historyIndex
|
const historyIndex = this.state.historyIndex
|
||||||
const historyItem = history[historyIndex]
|
const historyItem = history[historyIndex]
|
||||||
|
|
||||||
const result = patchEson(this.state.data, historyItem.undo)
|
const result = patchEson(this.state.eson, historyItem.undo)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
data: result.data,
|
eson: result.data,
|
||||||
history,
|
history,
|
||||||
historyIndex: historyIndex + 1
|
historyIndex: historyIndex + 1
|
||||||
})
|
})
|
||||||
|
@ -816,10 +816,10 @@ export default class TreeMode extends Component {
|
||||||
const historyIndex = this.state.historyIndex - 1
|
const historyIndex = this.state.historyIndex - 1
|
||||||
const historyItem = history[historyIndex]
|
const historyItem = history[historyIndex]
|
||||||
|
|
||||||
const result = patchEson(this.state.data, historyItem.redo)
|
const result = patchEson(this.state.eson, historyItem.redo)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
data: result.data,
|
eson: result.data,
|
||||||
history,
|
history,
|
||||||
historyIndex
|
historyIndex
|
||||||
})
|
})
|
||||||
|
@ -845,10 +845,10 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const expand = options.expand || (path => this.expandKeepOrExpandAll(path))
|
const expand = options.expand || (path => this.expandKeepOrExpandAll(path))
|
||||||
const result = patchEson(this.state.data, actions, expand)
|
const result = patchEson(this.state.eson, actions, expand)
|
||||||
const data = result.data
|
const eson = result.data
|
||||||
|
|
||||||
if (this.props.history != false) {
|
if (this.props.history !== false) {
|
||||||
// update data and store history
|
// update data and store history
|
||||||
const historyItem = {
|
const historyItem = {
|
||||||
redo: actions,
|
redo: actions,
|
||||||
|
@ -860,21 +860,21 @@ export default class TreeMode extends Component {
|
||||||
.slice(0, MAX_HISTORY_ITEMS)
|
.slice(0, MAX_HISTORY_ITEMS)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
data,
|
eson,
|
||||||
history,
|
history,
|
||||||
historyIndex: 0
|
historyIndex: 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// update data and don't store history
|
// update data and don't store history
|
||||||
this.setState({ data })
|
this.setState({ eson })
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
patch: actions,
|
patch: actions,
|
||||||
revert: result.revert,
|
revert: result.revert,
|
||||||
error: result.error,
|
error: result.error,
|
||||||
data // FIXME: shouldn't pass data here
|
data: eson // FIXME: shouldn't pass data here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,7 +973,7 @@ export default class TreeMode extends Component {
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
*/
|
*/
|
||||||
exists (path) {
|
exists (path) {
|
||||||
return pathExists(this.state.data, path)
|
return pathExists(this.state.eson, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
276
src/eson.js
276
src/eson.js
|
@ -15,7 +15,7 @@ import initial from 'lodash/initial'
|
||||||
import last from 'lodash/last'
|
import last from 'lodash/last'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ESON, ESONObject, ESONArrayItem, ESONPointer, Selection, ESONType, ESONPath,
|
ESON, ESONObject, ESONArrayItem, ESONPointer, Selection, ESONPath,
|
||||||
Path,
|
Path,
|
||||||
JSONPath, JSONType
|
JSONPath, JSONType
|
||||||
} from './types'
|
} from './types'
|
||||||
|
@ -29,6 +29,15 @@ export const SELECTED_AFTER = 4
|
||||||
|
|
||||||
export const META = Symbol('meta')
|
export const META = Symbol('meta')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand function which will expand all nodes
|
||||||
|
* @param {Path} path
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export function expandAll (path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {JSONType} json
|
* @param {JSONType} json
|
||||||
|
@ -58,57 +67,6 @@ export function jsonToEson (json, path = []) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Expand function which will expand all nodes
|
|
||||||
* @param {Path} path
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
export function expandAll (path) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a JSON object into ESON
|
|
||||||
* @param {Object | Array | string | number | boolean | null} json
|
|
||||||
* @param {function(path: JSONPath)} [expand]
|
|
||||||
* @param {JSONPath} [path=[]]
|
|
||||||
* @param {ESONType} [type='value'] Optional eson type for the created value
|
|
||||||
* @return {ESON}
|
|
||||||
*/
|
|
||||||
export function jsonToEsonOld (json, expand = expandAll, path: JSONPath = [], type: ESONType = 'value') : ESON {
|
|
||||||
if (Array.isArray(json)) {
|
|
||||||
return {
|
|
||||||
type: 'Array',
|
|
||||||
expanded: expand(path),
|
|
||||||
items: json.map((child, index) => {
|
|
||||||
return {
|
|
||||||
id: createId(), // TODO: use id based on index (only has to be unique within this array)
|
|
||||||
value: jsonToEsonOld(child, expand, path.concat(index))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isObject(json)) {
|
|
||||||
return {
|
|
||||||
type: 'Object',
|
|
||||||
expanded: expand(path),
|
|
||||||
props: Object.keys(json).map((name, index) => {
|
|
||||||
return {
|
|
||||||
id: createId(), // TODO: use id based on index (only has to be unique within this array)
|
|
||||||
name,
|
|
||||||
value: jsonToEsonOld(json[name], expand, path.concat(name))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // value
|
|
||||||
return {
|
|
||||||
type: (type === 'string') ? 'string' : 'value',
|
|
||||||
value: json
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an ESON object to a JSON object
|
* Convert an ESON object to a JSON object
|
||||||
* @param {ESON} eson
|
* @param {ESON} eson
|
||||||
|
@ -133,103 +91,6 @@ export function esonToJson (eson: ESON) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a path of a JSON object into a path in the corresponding ESON object
|
|
||||||
* @param {ESON} eson
|
|
||||||
* @param {JSONPath} path
|
|
||||||
* @return {ESONPath} esonPath
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export function toEsonPath (eson: ESON, path: JSONPath) : ESONPath {
|
|
||||||
if (path.length === 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eson.type === 'Array') {
|
|
||||||
// index of an array
|
|
||||||
const index = path[0]
|
|
||||||
const item = eson.items[parseInt(index)]
|
|
||||||
if (!item) {
|
|
||||||
throw new Error('Array item "' + index + '" not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['items', String(index), 'value'].concat(toEsonPath(item.value, path.slice(1)))
|
|
||||||
}
|
|
||||||
else if (eson.type === 'Object') {
|
|
||||||
// object property. find the index of this property
|
|
||||||
const index = findPropertyIndex(eson, path[0])
|
|
||||||
const prop = eson.props[index]
|
|
||||||
if (!prop) {
|
|
||||||
throw new Error('Object property "' + path[0] + '" not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['props', String(index), 'value']
|
|
||||||
.concat(toEsonPath(prop.value, path.slice(1)))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert an ESON object to a JSON object
|
|
||||||
* @param {ESON} eson
|
|
||||||
* @param {ESONPath} esonPath
|
|
||||||
* @return {JSONPath} path
|
|
||||||
*/
|
|
||||||
export function toJsonPath (eson: ESON, esonPath: ESONPath) : JSONPath {
|
|
||||||
if (esonPath.length === 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eson.type === 'Array') {
|
|
||||||
// index of an array
|
|
||||||
const index = esonPath[1]
|
|
||||||
const item = eson.items[parseInt(index)]
|
|
||||||
if (!item) {
|
|
||||||
throw new Error('Array item "' + index + '" not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
return [index].concat(toJsonPath(item.value, esonPath.slice(3)))
|
|
||||||
}
|
|
||||||
else if (eson.type === 'Object') {
|
|
||||||
// object property. find the index of this property
|
|
||||||
const index = esonPath[1]
|
|
||||||
const prop = eson.props[parseInt(index)]
|
|
||||||
if (!prop) {
|
|
||||||
throw new Error('Object property "' + esonPath[1] + '" not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
return [prop.name].concat(toJsonPath(prop.value, esonPath.slice(3)))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a nested property from an ESON object using a JSON path
|
|
||||||
*/
|
|
||||||
export function getInEson (eson: ESON, jsonPath: JSONPath) {
|
|
||||||
return getIn(eson, toEsonPath(eson, jsonPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of a nested property in an ESON object using a JSON path
|
|
||||||
*/
|
|
||||||
export function setInEson (eson: ESON, jsonPath: JSONPath, value: JSONType) {
|
|
||||||
return setIn(eson, toEsonPath(eson, jsonPath), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of a nested property in an ESON object using a JSON path
|
|
||||||
*/
|
|
||||||
export function deleteInEson (eson: ESON, jsonPath: JSONPath) : JSONType {
|
|
||||||
// with initial we remove the 'value' property,
|
|
||||||
// we want to remove the whole item from the items array
|
|
||||||
return deleteIn(eson, initial(toEsonPath(eson, jsonPath)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform an eson object, traverse over the whole object (excluding the _meta)
|
* Transform an eson object, traverse over the whole object (excluding the _meta)
|
||||||
* objects, and allow replacing Objects/Arrays/values
|
* objects, and allow replacing Objects/Arrays/values
|
||||||
|
@ -569,14 +430,19 @@ export function applySelection (eson, selection) {
|
||||||
* Find the min and max index of a start and end child.
|
* Find the min and max index of a start and end child.
|
||||||
* Start and end can be a property name in case of an Object,
|
* Start and end can be a property name in case of an Object,
|
||||||
* or a matrix index (string with a number) in case of an Array.
|
* or a matrix index (string with a number) in case of an Array.
|
||||||
|
*
|
||||||
|
* @param {ESON} root
|
||||||
|
* @param {Path} rootPath
|
||||||
|
* @param {Selection} selection
|
||||||
|
* @return {{minIndex: number, maxIndex: number}}
|
||||||
*/
|
*/
|
||||||
export function findSelectionIndices (root: ESON, rootPath: JSONPath, selection: Selection) : { minIndex: number, maxIndex: number } {
|
export function findSelectionIndices (root, rootPath, selection) {
|
||||||
const start = (selection.after || selection.before || selection.start)[rootPath.length]
|
const start = (selection.after || selection.before || selection.start)[rootPath.length]
|
||||||
const end = (selection.after || selection.before || selection.end)[rootPath.length]
|
const end = (selection.after || selection.before || selection.end)[rootPath.length]
|
||||||
|
|
||||||
// if no object we assume it's an Array
|
// if no object we assume it's an Array
|
||||||
const startIndex = root.type === 'Object' ? findPropertyIndex(root, start) : parseInt(start)
|
const startIndex = root[META].type === 'Object' ? root[META].keys.indexOf(start) : parseInt(start)
|
||||||
const endIndex = root.type === 'Object' ? findPropertyIndex(root, end) : parseInt(end)
|
const endIndex = root[META].type === 'Object' ? root[META].keys.indexOf(end) : parseInt(end)
|
||||||
|
|
||||||
const minIndex = Math.min(startIndex, endIndex)
|
const minIndex = Math.min(startIndex, endIndex)
|
||||||
const maxIndex = Math.max(startIndex, endIndex) +
|
const maxIndex = Math.max(startIndex, endIndex) +
|
||||||
|
@ -588,15 +454,15 @@ export function findSelectionIndices (root: ESON, rootPath: JSONPath, selection:
|
||||||
/**
|
/**
|
||||||
* Get the JSON paths from a selection, sorted from first to last
|
* Get the JSON paths from a selection, sorted from first to last
|
||||||
*/
|
*/
|
||||||
export function pathsFromSelection (eson: ESON, selection: Selection): JSONPath[] {
|
export function pathsFromSelection (eson, selection: Selection): JSONPath[] {
|
||||||
// find the parent node shared by both start and end of the selection
|
// find the parent node shared by both start and end of the selection
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const root = getInEson(eson, rootPath)
|
const root = getIn(eson, rootPath)
|
||||||
|
|
||||||
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
|
|
||||||
if (root.type === 'Object') {
|
if (root[META].type === 'Object') {
|
||||||
return times(maxIndex - minIndex, i => rootPath.concat(root.props[i + minIndex].name))
|
return times(maxIndex - minIndex, i => rootPath.concat(root[META].keys[i + minIndex]))
|
||||||
}
|
}
|
||||||
else { // root.type === 'Array'
|
else { // root.type === 'Array'
|
||||||
return times(maxIndex - minIndex, i => rootPath.concat(String(i + minIndex)))
|
return times(maxIndex - minIndex, i => rootPath.concat(String(i + minIndex)))
|
||||||
|
@ -611,10 +477,9 @@ export function pathsFromSelection (eson: ESON, selection: Selection): JSONPath[
|
||||||
*/
|
*/
|
||||||
export function contentsFromPaths (data: ESON, paths: JSONPath[]) {
|
export function contentsFromPaths (data: ESON, paths: JSONPath[]) {
|
||||||
return paths.map(path => {
|
return paths.map(path => {
|
||||||
const esonPath = toEsonPath(data, path)
|
|
||||||
return {
|
return {
|
||||||
name: getIn(data, initial(esonPath).concat('name')) || String(esonPath[esonPath.length - 2]),
|
name: getIn(data, last(path)),
|
||||||
value: esonToJson(getIn(data, esonPath))
|
value: esonToJson(getIn(data, path))
|
||||||
// FIXME: also store the type and expanded state
|
// FIXME: also store the type and expanded state
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -659,93 +524,6 @@ function findSharedPath (path1: JSONPath, path2: JSONPath): JSONPath {
|
||||||
|
|
||||||
return path1.slice(0, i)
|
return path1.slice(0, i)
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Recursively transform ESON: a recursive "map" function
|
|
||||||
// * @param {ESON} eson
|
|
||||||
// * @param {function(value: ESON, path: Path, root: ESON)} callback
|
|
||||||
// * @return {ESON} Returns the transformed eson object
|
|
||||||
// */
|
|
||||||
// export function transform (eson: ESON, callback: RecurseCallback) : ESON {
|
|
||||||
// return recurseTransform (eson, [], eson, callback)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Recursively transform ESON
|
|
||||||
// * @param {ESON} value
|
|
||||||
// * @param {JSONPath} path
|
|
||||||
// * @param {ESON} root The root object, object at path=[]
|
|
||||||
// * @param {function(value: ESON, path: Path, root: ESON)} callback
|
|
||||||
// * @return {ESON} Returns the transformed eson object
|
|
||||||
// */
|
|
||||||
// function recurseTransform (value: ESON, path: JSONPath, root: ESON, callback: RecurseCallback) : ESON {
|
|
||||||
// let updatedValue: ESON = callback(value, path, root)
|
|
||||||
//
|
|
||||||
// if (value.type === 'Array') {
|
|
||||||
// let updatedItems = updatedValue.items
|
|
||||||
//
|
|
||||||
// updatedValue.items.forEach((item, index) => {
|
|
||||||
// const updatedItem = recurseTransform(item.value, path.concat(String(index)), root, callback)
|
|
||||||
// updatedItems = setIn(updatedItems, [index, 'value'], updatedItem)
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// updatedValue = setIn(updatedValue, ['items'], updatedItems)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (value.type === 'Object') {
|
|
||||||
// let updatedProps = updatedValue.props
|
|
||||||
//
|
|
||||||
// updatedValue.props.forEach((prop, index) => {
|
|
||||||
// const updatedItem = recurseTransform(prop.value, path.concat(prop.name), root, callback)
|
|
||||||
// updatedProps = setIn(updatedProps, [index, 'value'], updatedItem)
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// updatedValue = setIn(updatedValue, ['props'], updatedProps)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // (for type 'string' or 'value' there are no childs to traverse)
|
|
||||||
//
|
|
||||||
// return updatedValue
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively loop over a ESON object: a recursive "forEach" function.
|
|
||||||
* @param {ESON} eson
|
|
||||||
* @param {function(value: ESON, path: JSONPath, root: ESON)} callback
|
|
||||||
*/
|
|
||||||
export function traverse (eson: ESON, callback: RecurseCallback) {
|
|
||||||
return recurseTraverse (eson, [], eson, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively traverse a ESON object
|
|
||||||
* @param {ESON} value
|
|
||||||
* @param {JSONPath} path
|
|
||||||
* @param {ESON | null} root The root object, object at path=[]
|
|
||||||
* @param {function(value: ESON, path: Path, root: ESON)} callback
|
|
||||||
*/
|
|
||||||
function recurseTraverse (value: ESON, path: JSONPath, root: ESON, callback: RecurseCallback) {
|
|
||||||
callback(value, path, root)
|
|
||||||
|
|
||||||
switch (value.type) {
|
|
||||||
case 'Array': {
|
|
||||||
value.items.forEach((item: ESONArrayItem, index) => {
|
|
||||||
recurseTraverse(item.value, path.concat(String(index)), root, callback)
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Object': {
|
|
||||||
value.props.forEach((prop) => {
|
|
||||||
recurseTraverse(prop.value, path.concat(prop.name), root, callback)
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // type 'string' or 'value'
|
|
||||||
// no childs to traverse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether a path exists in the eson object
|
* Test whether a path exists in the eson object
|
||||||
|
@ -808,12 +586,12 @@ export function findNextProp (parent, prop) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the index of a property
|
* Find the index of a property
|
||||||
* @param {ESONObject} object
|
* @param {ESON} object
|
||||||
* @param {string} prop
|
* @param {string} prop
|
||||||
* @return {number} Returns the index when found, -1 when not found
|
* @return {number} Returns the index when found, -1 when not found
|
||||||
*/
|
*/
|
||||||
export function findPropertyIndex (object: ESONObject, prop: string) {
|
export function findPropertyIndex (object, prop) {
|
||||||
return object.props.findIndex(p => p.name === prop)
|
return object[META].keys.indexOf(prop)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move parseJSONPointer and compileJSONPointer to a separate file
|
// TODO: move parseJSONPointer and compileJSONPointer to a separate file
|
||||||
|
|
|
@ -3,7 +3,7 @@ import test from 'ava'
|
||||||
import { setIn, getIn, deleteIn } from '../src/utils/immutabilityHelpers'
|
import { setIn, getIn, deleteIn } from '../src/utils/immutabilityHelpers'
|
||||||
import {
|
import {
|
||||||
META,
|
META,
|
||||||
esonToJson, toEsonPath, toJsonPath, pathExists, transform, traverse,
|
esonToJson, pathExists, transform,
|
||||||
parseJSONPointer, compileJSONPointer,
|
parseJSONPointer, compileJSONPointer,
|
||||||
jsonToEson,
|
jsonToEson,
|
||||||
expand, expandOne, expandPath, applyErrors, search, nextSearchResult,
|
expand, expandOne, expandPath, applyErrors, search, nextSearchResult,
|
||||||
|
@ -15,8 +15,6 @@ import 'console.table'
|
||||||
import repeat from 'lodash/repeat'
|
import repeat from 'lodash/repeat'
|
||||||
import { assertDeepEqualEson } from './utils/assertDeepEqualEson'
|
import { assertDeepEqualEson } from './utils/assertDeepEqualEson'
|
||||||
|
|
||||||
const JSON1 = loadJSON('./resources/json1.json')
|
|
||||||
const ESON1 = loadJSON('./resources/eson1.json')
|
|
||||||
const ESON2 = loadJSON('./resources/eson2.json')
|
const ESON2 = loadJSON('./resources/eson2.json')
|
||||||
|
|
||||||
test('jsonToEson', t => {
|
test('jsonToEson', t => {
|
||||||
|
@ -236,33 +234,6 @@ test('add and remove errors', t => {
|
||||||
t.is(actual3.str, eson.str) // shouldn't have touched values not affected by the errors
|
t.is(actual3.str, eson.str) // shouldn't have touched values not affected by the errors
|
||||||
})
|
})
|
||||||
|
|
||||||
test('traverse', t => {
|
|
||||||
// {obj: {a: 2}, arr: [3]}
|
|
||||||
|
|
||||||
let log = []
|
|
||||||
const returnValue = traverse(ESON2, function (value, path, root) {
|
|
||||||
t.is(root, ESON2)
|
|
||||||
|
|
||||||
log.push([value, path, root])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.is(returnValue, undefined)
|
|
||||||
|
|
||||||
const EXPECTED_LOG = [
|
|
||||||
[ESON2, [], ESON2],
|
|
||||||
[ESON2.props[0].value, ['obj'], ESON2],
|
|
||||||
[ESON2.props[0].value.props[0].value, ['obj', 'a'], ESON2],
|
|
||||||
[ESON2.props[1].value, ['arr'], ESON2],
|
|
||||||
[ESON2.props[1].value.items[0].value, ['arr', '0'], ESON2],
|
|
||||||
]
|
|
||||||
|
|
||||||
log.forEach((row, index) => {
|
|
||||||
t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
|
||||||
})
|
|
||||||
t.deepEqual(log, EXPECTED_LOG)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
test('search', t => {
|
test('search', t => {
|
||||||
const eson = jsonToEson({
|
const eson = jsonToEson({
|
||||||
"obj": {
|
"obj": {
|
||||||
|
@ -476,12 +447,20 @@ test('selection (node)', t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('pathsFromSelection (object)', t => {
|
test('pathsFromSelection (object)', t => {
|
||||||
|
const eson = jsonToEson({
|
||||||
|
"obj": {
|
||||||
|
"arr": [1,2, {"first":3,"last":4}]
|
||||||
|
},
|
||||||
|
"str": "hello world",
|
||||||
|
"nill": null,
|
||||||
|
"bool": false
|
||||||
|
})
|
||||||
const selection = {
|
const selection = {
|
||||||
start: ['obj', 'arr', '2', 'last'],
|
start: ['obj', 'arr', '2', 'last'],
|
||||||
end: ['nill']
|
end: ['nill']
|
||||||
}
|
}
|
||||||
|
|
||||||
t.deepEqual(pathsFromSelection(ESON1, selection), [
|
t.deepEqual(pathsFromSelection(eson, selection), [
|
||||||
['obj'],
|
['obj'],
|
||||||
['str'],
|
['str'],
|
||||||
['nill']
|
['nill']
|
||||||
|
@ -489,42 +468,74 @@ test('pathsFromSelection (object)', t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('pathsFromSelection (array)', t => {
|
test('pathsFromSelection (array)', t => {
|
||||||
|
const eson = jsonToEson({
|
||||||
|
"obj": {
|
||||||
|
"arr": [1,2, {"first":3,"last":4}]
|
||||||
|
},
|
||||||
|
"str": "hello world",
|
||||||
|
"nill": null,
|
||||||
|
"bool": false
|
||||||
|
})
|
||||||
const selection = {
|
const selection = {
|
||||||
start: ['obj', 'arr', '1'],
|
start: ['obj', 'arr', '1'],
|
||||||
end: ['obj', 'arr', '0'] // note the "wrong" order of start and end
|
end: ['obj', 'arr', '0'] // note the "wrong" order of start and end
|
||||||
}
|
}
|
||||||
|
|
||||||
t.deepEqual(pathsFromSelection(ESON1, selection), [
|
t.deepEqual(pathsFromSelection(eson, selection), [
|
||||||
['obj', 'arr', '0'],
|
['obj', 'arr', '0'],
|
||||||
['obj', 'arr', '1']
|
['obj', 'arr', '1']
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('pathsFromSelection (value)', t => {
|
test('pathsFromSelection (value)', t => {
|
||||||
|
const eson = jsonToEson({
|
||||||
|
"obj": {
|
||||||
|
"arr": [1,2, {"first":3,"last":4}]
|
||||||
|
},
|
||||||
|
"str": "hello world",
|
||||||
|
"nill": null,
|
||||||
|
"bool": false
|
||||||
|
})
|
||||||
const selection = {
|
const selection = {
|
||||||
start: ['obj', 'arr', '2', 'first'],
|
start: ['obj', 'arr', '2', 'first'],
|
||||||
end: ['obj', 'arr', '2', 'first']
|
end: ['obj', 'arr', '2', 'first']
|
||||||
}
|
}
|
||||||
|
|
||||||
t.deepEqual(pathsFromSelection(ESON1, selection), [
|
t.deepEqual(pathsFromSelection(eson, selection), [
|
||||||
['obj', 'arr', '2', 'first'],
|
['obj', 'arr', '2', 'first'],
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('pathsFromSelection (before)', t => {
|
test('pathsFromSelection (before)', t => {
|
||||||
|
const eson = jsonToEson({
|
||||||
|
"obj": {
|
||||||
|
"arr": [1,2, {"first":3,"last":4}]
|
||||||
|
},
|
||||||
|
"str": "hello world",
|
||||||
|
"nill": null,
|
||||||
|
"bool": false
|
||||||
|
})
|
||||||
const selection = {
|
const selection = {
|
||||||
before: ['obj', 'arr', '2', 'first']
|
before: ['obj', 'arr', '2', 'first']
|
||||||
}
|
}
|
||||||
|
|
||||||
t.deepEqual(pathsFromSelection(ESON1, selection), [])
|
t.deepEqual(pathsFromSelection(eson, selection), [])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('pathsFromSelection (after)', t => {
|
test('pathsFromSelection (after)', t => {
|
||||||
|
const eson = jsonToEson({
|
||||||
|
"obj": {
|
||||||
|
"arr": [1,2, {"first":3,"last":4}]
|
||||||
|
},
|
||||||
|
"str": "hello world",
|
||||||
|
"nill": null,
|
||||||
|
"bool": false
|
||||||
|
})
|
||||||
const selection = {
|
const selection = {
|
||||||
after: ['obj', 'arr', '2', 'first']
|
after: ['obj', 'arr', '2', 'first']
|
||||||
}
|
}
|
||||||
|
|
||||||
t.deepEqual(pathsFromSelection(ESON1, selection), [])
|
t.deepEqual(pathsFromSelection(eson, selection), [])
|
||||||
})
|
})
|
||||||
|
|
||||||
// helper function to print JSON in the console
|
// helper function to print JSON in the console
|
||||||
|
|
Loading…
Reference in New Issue