Keep expanded state when patching data

This commit is contained in:
jos 2016-10-30 17:31:16 +01:00
parent 3f3fef9b40
commit f882756dda
4 changed files with 69 additions and 22 deletions

View File

@ -1,7 +1,7 @@
import { h, Component } from 'preact'
import { updateIn } from '../utils/immutabilityHelpers'
import { expand, jsonToData, dataToJson, toDataPath, patchData } from '../jsonData'
import { updateIn, getIn } from '../utils/immutabilityHelpers'
import { expand, jsonToData, dataToJson, toDataPath, patchData, pathExists } from '../jsonData'
import { parseJSON } from '../utils/jsonUtils'
import {
duplicate, insert, append, remove, changeType, changeValue, changeProperty, sort
@ -17,8 +17,7 @@ export default class TreeMode extends Component {
constructor (props) {
super(props)
const expand = this.props.options.expand || TreeMode.expand
const data = jsonToData(this.props.data || {}, expand, [])
const data = jsonToData(this.props.data || {}, TreeMode.expandAll, [])
this.state = {
data,
@ -282,13 +281,22 @@ export default class TreeMode extends Component {
/**
* Apply a JSONPatch to the current JSON document
* @param {JSONPatch} actions JSONPatch actions
* @param {JSONPatch} actions JSONPatch actions
* @param {PatchOptions} [options] If no expand function is provided, the
* expanded state will be kept as is for
* existing paths. New paths will be fully
* expanded.
* @return {JSONPatchResult} Returns a JSONPatch result containing the
* patch, a patch to revert the action, and
* an error object which is null when successful
*/
patch (actions) {
const result = patchData(this.state.data, actions)
patch (actions, options = {}) {
if (!Array.isArray(actions)) {
throw new TypeError('Array with patch actions expected')
}
const expand = options.expand || (path => this.expandKeepOrExpandAll(path))
const result = patchData(this.state.data, actions, expand)
const data = result.data
if (this.props.options.history != false) {
@ -323,13 +331,17 @@ export default class TreeMode extends Component {
/**
* Set JSON object in editor
* @param {Object | Array | string | number | boolean | null} json JSON data
* @param {SetOptions} [options]
* @param {SetOptions} [options] If no expand function is provided,
* The root will be expanded and all other nodes
* will be collapsed.
*/
set (json, options = {}) {
this.setState({
data: jsonToData(json, options.expand || TreeMode.expand, []),
const expand = options.expand || TreeMode.expandRoot
// TODO: do we want to keep history when .set(json) is called?
this.setState({
data: jsonToData(json, expand, []),
// TODO: do we want to keep history when .set(json) is called? (currently we remove history)
history: [],
historyIndex: 0
})
@ -380,6 +392,36 @@ export default class TreeMode extends Component {
})
}
/**
* Test whether a path exists in the editor
* @param {Path} path
*/
exists (path) {
return pathExists(this.state.data, path)
}
/**
* Test whether an Array or Object at a certain path is expanded.
* When the node does not exist, the function throws an error
* @param {Path} path
* @return {boolean} Returns true when expanded, false otherwise
*/
isExpanded (path) {
return getIn(this.state.data, toDataPath(this.state.data, path)).expanded
}
/**
* Expand function which keeps the expanded state the same as the current data.
* When the path doesn't yet exist, it will be expanded.
* @param {Path} path
* @return {boolean}
*/
expandKeepOrExpandAll (path) {
return this.exists(path)
? this.isExpanded(path)
: TreeMode.expandAll(path)
}
/**
* Destroy the editor
*/
@ -395,11 +437,10 @@ export default class TreeMode extends Component {
* @param {Array.<string>} path
* @return {boolean}
*/
static expand (path) {
static expandRoot (path) {
return path.length === 0
}
/**
* Callback function to expand all nodes
*

View File

@ -13,7 +13,7 @@
<style>
#container {
height: 300px;
height: 500px;
width: 100%;
max-width : 800px;
}

View File

@ -7,12 +7,17 @@ import { setIn, updateIn, getIn, deleteIn, insertAt } from './utils/immutability
import { isObject } from './utils/typeUtils'
import isEqual from 'lodash/isEqual'
const expandAll = function (path) {
/**
* Expand function which will expand all nodes
* @param path
* @return {boolean}
*/
export function expandAll (path) {
return true
}
// TODO: double check whether all patch functions handle each of the
// extra properties in .jsoneditor: `before`, `type`, `order`
// extra properties in .jsoneditor: `before`, `type`, ...
/**
* Convert a JSON object into the internally used data model
@ -112,11 +117,11 @@ export function toDataPath (data, path) {
* Apply a patch to a JSONData object
* @param {JSONData} data
* @param {Array} patch A JSON patch
* @param {function(path: Path)} [expand] Optional function to determine
* what nodes must be expanded
* @return {{data: JSONData, revert: Array.<Object>, error: Error | null}}
*/
export function patchData (data, patch) {
const expand = expandAll // TODO: customizable expand function
export function patchData (data, patch, expand = expandAll) {
let updatedData = data
let revert = []
@ -134,7 +139,6 @@ export function patchData (data, patch) {
// insert with type 'string' or 'value'
newValue.type = options.type
}
// TODO: handle options.order
const result = add(updatedData, action.path, newValue, options)
updatedData = result.data
@ -155,12 +159,10 @@ export function patchData (data, patch) {
const path = parseJSONPointer(action.path)
let newValue = jsonToData(action.value, expand, path)
// TODO: move setting type to jsonToData
if (options && options.type) {
// insert with type 'string' or 'value'
newValue.type = options.type
}
// TODO: handle options.order
const result = replace(updatedData, path, newValue)
updatedData = result.data

View File

@ -48,4 +48,8 @@
* @typedef {{
* expand: function (path: Path)?
* }} SetOptions
*
* @typedef {{
* expand: function (path: Path)?
* }} PatchOptions
*/