Improved expand function, fixed expand/collapse of TreeMode

This commit is contained in:
jos 2017-12-27 18:18:23 +01:00
parent e5c6459f73
commit 8b2541e3d8
4 changed files with 65 additions and 36 deletions

View File

@ -62,7 +62,7 @@ export default class TextMode extends Component {
this.applyProps(nextProps, this.props)
}
// TODO: create some sort of watcher structure for these props? Is there a Reactpattern for that?
// TODO: create some sort of watcher structure for these props? Is there a React pattern for that?
applyProps (nextProps, currentProps) {
// Apply text
if (nextProps.text !== currentProps.text) {
@ -87,6 +87,7 @@ export default class TextMode extends Component {
this.findKeyBinding = createFindKeyBinding(keyBindings)
}
// TODO: apply patch
// TODO: apply patchText
}

View File

@ -156,9 +156,9 @@ export default class TreeMode extends PureComponent {
// Apply json
if (nextProps.json !== this.state.json) {
// FIXME: merge meta data from existing eson
const expandCallback = this.props.expand || TreeMode.expandRoot
const callback = this.props.expand || TreeMode.expandRoot
const json = nextProps.json
const eson = expand(jsonToEson(json), expandCallback)
const eson = expand(jsonToEson(json), callback)
this.setState({
json,
@ -571,7 +571,7 @@ export default class TreeMode extends PureComponent {
if (recurse) {
this.setState({
eson: updateIn(this.state.eson, path, function (child) {
return expand(child, (path) => true, expanded)
return expand(child, (path) => expanded)
})
})
}
@ -588,18 +588,14 @@ export default class TreeMode extends PureComponent {
}
handleExpandAll = () => {
const expanded = true
this.setState({
eson: expand(this.state.eson, TreeMode.expandAll, expanded)
eson: expand(this.state.eson, TreeMode.expandAll)
})
}
handleCollapseAll = () => {
const expanded = false
this.setState({
eson: expand(this.state.eson, TreeMode.expandAll, expanded)
eson: expand(this.state.eson, TreeMode.collapseAll)
})
}
@ -1008,20 +1004,38 @@ export default class TreeMode extends PureComponent {
* @param {Path | function (path: Path) : boolean} callback
*/
expand (callback) {
if (Array.isArray(callback)) {
this.setState({
eson: expand(this.state.eson, callback, true)
eson: expandPath(this.state.eson, callback, true)
})
}
else { // callback is a function
this.setState({
eson: expand(this.state.eson, (path) => {
return callback(path) === true ? true : undefined
})
})
}
}
/**
* Collapse one or multiple objects or arrays
* @param {Path | function (path: Path) : boolean} callback
*/
collapse (callback) {
if (Array.isArray(callback)) {
this.setState({
eson: expand(this.state.eson, callback, false)
eson: expandPath(this.state.eson, callback, true)
})
}
else { // callback is a function
this.setState({
eson: expand(this.state.eson, (path) => {
return callback(path) === true ? false : undefined
})
})
}
}
/**
* Test whether a path exists in the editor
@ -1074,6 +1088,16 @@ export default class TreeMode extends PureComponent {
static expandAll (path) {
return true
}
/**
* Callback function to collapse all nodes
*
* @param {Array.<string>} path
* @return {boolean}
*/
static collapseAll (path) {
return false
}
}
// TODO: describe PropTypes

View File

@ -138,19 +138,23 @@ export function updatePaths(eson, path = []) {
/**
* Expand or collapse all items matching a filter callback
* @param {ESON} eson
* @param {function(Path) : boolean | Path} filterCallback
* When a path, the object/array at this path will be expanded/collapsed
* When a function, all objects and arrays for which callback
* returns true will be expanded/collapsed
* @param {boolean} [expanded=true] New expanded state: true to expand, false to collapse
* @param {function(Path) : boolean | undefined} callback
* All objects and arrays for which callback returns true will be expanded
* All objects and arrays for which callback returns false will be collapsed
* All objects and arrays for which callback returns undefined will be left as is
* @return {ESON}
*/
export function expand (eson, filterCallback, expanded = true) {
// TODO: adjust expand to have a filterCallback which can return true, false, or undefined. In the latter case, the expanded state is left as is.
export function expand (eson, callback) {
return transform(eson, function (value, path) {
return ((value[META].type === 'Array' || value[META].type === 'Object') && filterCallback(path))
? expandOne(value, [], expanded)
: value
if (value[META].type === 'Array' || value[META].type === 'Object') {
const expanded = callback(path)
return (typeof expanded === 'boolean')
? expandOne(value, [], expanded) // adjust expanded state
: value // leave as is when returned value is null or undefined
}
else {
return value
}
})
}

View File

@ -112,15 +112,16 @@ test('expand a callback', () => {
"bool": false
})
function filterCallback (path) {
return path.length >= 1
function callback (path) {
return (path.length >= 1)
? false // collapse
: undefined // leave untouched
}
const expandedValue = false
const collapsed = expand(eson, filterCallback, expandedValue)
const collapsed = expand(eson, callback)
expect(collapsed[META].expanded).toEqual(undefined)
expect(collapsed.obj[META].expanded).toEqual(expandedValue)
expect(collapsed.obj.arr[META].expanded).toEqual(expandedValue)
expect(collapsed.obj.arr[2][META].expanded).toEqual(expandedValue)
expect(collapsed.obj[META].expanded).toEqual(false)
expect(collapsed.obj.arr[META].expanded).toEqual(false)
expect(collapsed.obj.arr[2][META].expanded).toEqual(false)
let orig = collapsed
orig = deleteIn(orig, ['obj'].concat([META, 'expanded']))
@ -132,10 +133,9 @@ test('expand a callback', () => {
test('expand a callback should not change the object when nothing happens', () => {
const eson = jsonToEson({a: [1,2,3], b: {c: 4}})
function callback (path) {
return false
return undefined
}
const expanded = false
const collapsed = expand(eson, callback, expanded)
const collapsed = expand(eson, callback)
expect(collapsed).toBe(eson)
})