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) 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) { applyProps (nextProps, currentProps) {
// Apply text // Apply text
if (nextProps.text !== currentProps.text) { if (nextProps.text !== currentProps.text) {
@ -87,6 +87,7 @@ export default class TextMode extends Component {
this.findKeyBinding = createFindKeyBinding(keyBindings) this.findKeyBinding = createFindKeyBinding(keyBindings)
} }
// TODO: apply patch
// TODO: apply patchText // TODO: apply patchText
} }

View File

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

View File

@ -138,19 +138,23 @@ export function updatePaths(eson, path = []) {
/** /**
* Expand or collapse all items matching a filter callback * Expand or collapse all items matching a filter callback
* @param {ESON} eson * @param {ESON} eson
* @param {function(Path) : boolean | Path} filterCallback * @param {function(Path) : boolean | undefined} callback
* When a path, the object/array at this path will be expanded/collapsed * All objects and arrays for which callback returns true will be expanded
* When a function, all objects and arrays for which callback * All objects and arrays for which callback returns false will be collapsed
* returns true will be expanded/collapsed * All objects and arrays for which callback returns undefined will be left as is
* @param {boolean} [expanded=true] New expanded state: true to expand, false to collapse
* @return {ESON} * @return {ESON}
*/ */
export function expand (eson, filterCallback, expanded = true) { export function expand (eson, callback) {
// 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.
return transform(eson, function (value, path) { return transform(eson, function (value, path) {
return ((value[META].type === 'Array' || value[META].type === 'Object') && filterCallback(path)) if (value[META].type === 'Array' || value[META].type === 'Object') {
? expandOne(value, [], expanded) const expanded = callback(path)
: value 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 "bool": false
}) })
function filterCallback (path) { function callback (path) {
return path.length >= 1 return (path.length >= 1)
? false // collapse
: undefined // leave untouched
} }
const expandedValue = false const collapsed = expand(eson, callback)
const collapsed = expand(eson, filterCallback, expandedValue)
expect(collapsed[META].expanded).toEqual(undefined) expect(collapsed[META].expanded).toEqual(undefined)
expect(collapsed.obj[META].expanded).toEqual(expandedValue) expect(collapsed.obj[META].expanded).toEqual(false)
expect(collapsed.obj.arr[META].expanded).toEqual(expandedValue) expect(collapsed.obj.arr[META].expanded).toEqual(false)
expect(collapsed.obj.arr[2][META].expanded).toEqual(expandedValue) expect(collapsed.obj.arr[2][META].expanded).toEqual(false)
let orig = collapsed let orig = collapsed
orig = deleteIn(orig, ['obj'].concat([META, 'expanded'])) 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', () => { test('expand a callback should not change the object when nothing happens', () => {
const eson = jsonToEson({a: [1,2,3], b: {c: 4}}) const eson = jsonToEson({a: [1,2,3], b: {c: 4}})
function callback (path) { function callback (path) {
return false return undefined
} }
const expanded = false const collapsed = expand(eson, callback)
const collapsed = expand(eson, callback, expanded)
expect(collapsed).toBe(eson) expect(collapsed).toBe(eson)
}) })