diff --git a/src/actions.js b/src/actions.js index be419a4..928c5c6 100644 --- a/src/actions.js +++ b/src/actions.js @@ -1,11 +1,10 @@ import last from 'lodash/last' import initial from 'lodash/initial' import { - compileJSONPointer, toEsonPath, esonToJson, findNextProp, + compileJSONPointer, getInEson, esonToJson, findNextProp, pathsFromSelection, findRootPath, findSelectionIndices } from './eson' import { findUniqueName } from './utils/stringUtils' -import { getIn } from './utils/immutabilityHelpers' import { isObject, stringConvert } from './utils/typeUtils' import { compareAsc, compareDesc, strictShallowEqual } from './utils/arrayUtils' @@ -19,9 +18,7 @@ import { compareAsc, compareDesc, strictShallowEqual } from './utils/arrayUtils' */ export function changeValue (data, path, value) { // console.log('changeValue', data, value) - - const esonPath = toEsonPath(data, path) - const oldDataValue = getIn(data, esonPath) + const oldDataValue = getInEson(data, path) return [{ op: 'replace', @@ -43,9 +40,7 @@ export function changeValue (data, path, value) { */ export function changeProperty (data, parentPath, oldProp, newProp) { // console.log('changeProperty', parentPath, oldProp, newProp) - - const esonPath = toEsonPath(data, parentPath) - const parent = getIn(data, esonPath) + const parent = getInEson(data, parentPath) // prevent duplicate property names const uniqueNewProp = findUniqueName(newProp, parent.props.map(p => p.name)) @@ -68,8 +63,7 @@ export function changeProperty (data, parentPath, oldProp, newProp) { * @return {Array} */ export function changeType (data, path, type) { - const esonPath = toEsonPath(data, path) - const oldValue = esonToJson(getIn(data, esonPath)) + const oldValue = esonToJson(getInEson(data, path)) const newValue = convertType(oldValue, type) // console.log('changeType', path, type, oldValue, newValue) @@ -102,7 +96,7 @@ export function duplicate (data, selection) { } const rootPath = findRootPath(selection) - const root = getIn(data, toEsonPath(data, rootPath)) + const root = getInEson(data, rootPath) const { maxIndex } = findSelectionIndices(root, rootPath, selection) const paths = pathsFromSelection(data, selection) @@ -147,8 +141,7 @@ export function duplicate (data, selection) { */ export function insertBefore (data, path, values) { // TODO: find a better name and define datastructure for values const parentPath = initial(path) - const esonPath = toEsonPath(data, parentPath) - const parent = getIn(data, esonPath) + const parent = getInEson(data, parentPath) if (parent.type === 'Array') { const startIndex = parseInt(last(path)) @@ -192,7 +185,7 @@ export function insertBefore (data, path, values) { // TODO: find a better name */ export function replace (data, selection, values) { // TODO: find a better name and define datastructure for values const rootPath = findRootPath(selection) - const root = getIn(data, toEsonPath(data, rootPath)) + const root = getInEson(data, rootPath) const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection) if (root.type === 'Array') { @@ -245,8 +238,7 @@ export function replace (data, selection, values) { // TODO: find a better name export function append (data, parentPath, type) { // console.log('append', parentPath, value) - const esonPath = toEsonPath(data, parentPath) - const parent = getIn(data, esonPath) + const parent = getInEson(data, parentPath) const value = createEntry(type) if (parent.type === 'Array') { @@ -310,8 +302,7 @@ export function sort (data, path, order = null) { // console.log('sort', path, order) const compare = order === 'desc' ? compareDesc : compareAsc - const esonPath = toEsonPath(data, path) - const object = getIn(data, esonPath) + const object = getInEson(data, path) if (object.type === 'Array') { const orderedItems = object.items.slice(0) diff --git a/src/components/TreeMode.js b/src/components/TreeMode.js index 449fb47..9e268f7 100644 --- a/src/components/TreeMode.js +++ b/src/components/TreeMode.js @@ -4,17 +4,15 @@ import { createElement as h, Component } from 'react' import isEqual from 'lodash/isEqual' import reverse from 'lodash/reverse' import initial from 'lodash/initial' -import last from 'lodash/last' import Hammer from 'react-hammerjs' import jump from '../assets/jump.js/src/jump' import Ajv from 'ajv' -import { updateIn, getIn, setIn } from '../utils/immutabilityHelpers' +import { setIn } from '../utils/immutabilityHelpers' import { parseJSON } from '../utils/jsonUtils' -import { findUniqueName } from '../utils/stringUtils' import { enrichSchemaError } from '../utils/schemaUtils' import { - jsonToEson, esonToJson, toEsonPath, pathExists, + jsonToEson, esonToJson, getInEson, updateInEson, pathExists, expand, expandPath, addErrors, search, applySearchResults, nextSearchResult, previousSearchResult, applySelection, pathsFromSelection, contentsFromPaths, @@ -548,10 +546,8 @@ export default class TreeMode extends Component { handleExpand = (path, expanded, recurse) => { if (recurse) { - const esonPath = toEsonPath(this.state.data, path) - this.setState({ - data: updateIn(this.state.data, esonPath, function (child) { + data: updateInEson(this.state.data, path, function (child) { return expand(child, (path) => true, expanded) }) }) @@ -986,7 +982,7 @@ export default class TreeMode extends Component { * @return {boolean} Returns true when expanded, false otherwise */ isExpanded (path) { - return getIn(this.state.data, toEsonPath(this.state.data, path)).expanded + return getInEson(this.state.data, path).expanded } /** diff --git a/src/eson.js b/src/eson.js index 3121d2a..b0d9cf4 100644 --- a/src/eson.js +++ b/src/eson.js @@ -5,7 +5,7 @@ * All functions are pure and don't mutate the ESON. */ -import { setIn, getIn, updateIn } from './utils/immutabilityHelpers' +import { setIn, getIn, updateIn, deleteIn } from './utils/immutabilityHelpers' import { isObject } from './utils/typeUtils' import isEqual from 'lodash/isEqual' import times from 'lodash/times' @@ -174,7 +174,35 @@ export function toJsonPath (eson: ESON, esonPath: ESONPath) : JSONPath { } } -type ExpandCallback = (Path) => boolean +/** + * 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 updateInEson (eson: ESON, jsonPath: JSONPath, callback) { + return updateIn(eson, toEsonPath(eson, jsonPath), callback) +} + +/** + * 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))) +} /** * Expand or collapse one or multiple items or properties @@ -263,7 +291,7 @@ export function search (eson: ESON, text: string): ESONPointer[] { // only add search result when this is an object property name, // don't add search result for array indices const parentPath = initial(path) - const parent = getIn(eson, toEsonPath(eson, parentPath)) + const parent = getInEson(eson, parentPath) if (parent.type === 'Object') { results.push({path, area: 'property'}) } @@ -374,7 +402,7 @@ export function applySelection (eson: ESON, selection: Selection) { // find the parent node shared by both start and end of the selection const rootPath = findRootPath(selection) - return updateIn(eson, toEsonPath(eson, rootPath), (root) => { + return updateInEson(eson, rootPath, (root) => { const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection) const childsKey = (root.type === 'Object') ? 'props' : 'items' // property name of the array with props/items @@ -415,7 +443,7 @@ export function findSelectionIndices (root: ESON, rootPath: JSONPath, selection: export function pathsFromSelection (eson: ESON, selection: Selection): JSONPath[] { // find the parent node shared by both start and end of the selection const rootPath = findRootPath(selection) - const root = getIn(eson, toEsonPath(eson, rootPath)) + const root = getInEson(eson, rootPath) const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection) @@ -613,7 +641,7 @@ export function pathExists (eson: ESON, path: JSONPath) { export function resolvePathIndex (eson, path) { if (path[path.length - 1] === '-') { const parentPath = path.slice(0, path.length - 1) - const parent = getIn(eson, toEsonPath(eson, parentPath)) + const parent = getInEson(eson, parentPath) if (parent.type === 'Array') { const index = parent.items.length diff --git a/src/patchEson.js b/src/patchEson.js index 9f2724d..1c396c0 100644 --- a/src/patchEson.js +++ b/src/patchEson.js @@ -1,10 +1,12 @@ import isEqual from 'lodash/isEqual' import initial from 'lodash/initial' +import last from 'lodash/last' import type { ESON, Path, ESONPatch } from './types' -import { setIn, updateIn, getIn, deleteIn, insertAt } from './utils/immutabilityHelpers' +import { setIn, updateIn, getIn, insertAt } from './utils/immutabilityHelpers' import { jsonToEson, esonToJson, toEsonPath, + getInEson, setInEson, deleteInEson, parseJSONPointer, compileJSONPointer, expandAll, pathExists, resolvePathIndex, findPropertyIndex, findNextProp, getId } from './eson' @@ -126,11 +128,10 @@ export function patchEson (eson: ESON, patch: ESONPatch, expand = expandAll) { * @return {{data: ESON, revert: ESONPatch}} */ export function replace (data: ESON, path: Path, value: ESON) { - const esonPath = toEsonPath(data, path) - const oldValue = getIn(data, esonPath) + const oldValue = getInEson(data, path) return { - data: setIn(data, esonPath, value), + data: setInEson(data, path, value), revert: [{ op: 'replace', path: compileJSONPointer(path), @@ -148,23 +149,19 @@ export function replace (data: ESON, path: Path, value: ESON) { * @param {string} path * @return {{data: ESON, revert: ESONPatch}} */ +// FIXME: path should be a path instead of a string? (all functions in patchEson) export function remove (data: ESON, path: string) { // console.log('remove', path) const pathArray = parseJSONPointer(path) - const parentPath = pathArray.slice(0, pathArray.length - 1) - const parent = getIn(data, toEsonPath(data, parentPath)) - const dataValue = getIn(data, toEsonPath(data, pathArray)) + const parentPath = initial(pathArray) + const parent = getInEson(data, parentPath) + const dataValue = getInEson(data, pathArray) const value = esonToJson(dataValue) if (parent.type === 'Array') { - const esonPath = toEsonPath(data, pathArray) - - // remove the 'value' property, we want to remove the whole item from the items array - esonPath.pop() - return { - data: deleteIn(data, esonPath), + data: deleteInEson(data, pathArray), revert: [{ op: 'add', path, @@ -176,14 +173,10 @@ export function remove (data: ESON, path: string) { } } else { // object.type === 'Object' - const esonPath = toEsonPath(data, pathArray) - const prop = pathArray[pathArray.length - 1] - - // remove the 'value' property, we want to remove the whole object property from props - esonPath.pop() + const prop = last(pathArray) return { - data: deleteIn(data, esonPath), + data: deleteInEson(data, pathArray), revert: [{ op: 'add', path, @@ -274,7 +267,7 @@ export function add (data: ESON, path: string, value: ESON, options, id = getId( } if (parent.type === 'Object' && pathExists(data, resolvedPath)) { - const oldValue = getIn(data, toEsonPath(data, resolvedPath)) + const oldValue = getInEson(data, resolvedPath) return { data: updatedEson, @@ -307,7 +300,7 @@ export function add (data: ESON, path: string, value: ESON, options, id = getId( * @private */ export function copy (data: ESON, path: string, from: string, options) { - const value = getIn(data, toEsonPath(data, parseJSONPointer(from))) + const value = getInEson(data, parseJSONPointer(from)) return add(data, path, value, options) } @@ -329,7 +322,7 @@ export function move (data: ESON, path: string, from: string, options) { // FIXME: only reuse the existing id when move is renaming a property in the same object const parentPathFrom = initial(fromArray) - const parent = getIn(data, toEsonPath(data, parentPathFrom)) + const parent = getInEson(data, parentPathFrom) const result1 = remove(data, from) const result2 = add(result1.data, path, dataValue, options, id) @@ -379,7 +372,7 @@ export function test (data: ESON, path: string, value: any) { return new Error('Test failed, path not found') } - const actualValue = getIn(data, toEsonPath(data, pathArray)) + const actualValue = getInEson(data, pathArray) if (!isEqual(esonToJson(actualValue), value)) { return new Error('Test failed, value differs') }