Changed internal path to Array again

This commit is contained in:
jos 2016-07-30 20:30:08 +02:00
parent 667d3f32aa
commit 5416676735
2 changed files with 60 additions and 78 deletions

View File

@ -2,6 +2,7 @@ import { h, Component } from 'preact'
import ContextMenu from './ContextMenu'
import { escapeHTML, unescapeHTML } from './utils/stringUtils'
import { last } from './utils/arrayUtils'
import { getInnerText } from './utils/domUtils'
import {stringConvert, valueType, isUrl} from './utils/typeUtils'
@ -45,13 +46,13 @@ export default class JSONNode extends Component {
}
}
renderJSONObject ({data, path, index, options, events}) {
renderJSONObject ({path, data, options, events}) {
const childCount = data.childs.length
const contents = [
h('div', {class: 'jsoneditor-node jsoneditor-object'}, [
this.renderExpandButton(),
this.renderContextMenuButton(),
this.renderProperty(data, index, options),
this.renderProperty(path, data, options),
this.renderSeparator(), // TODO: remove separator for Object and Array (gives an issue in Preact)
this.renderReadonly(`{${childCount}}`, `Array containing ${childCount} items`)
])
@ -60,7 +61,7 @@ export default class JSONNode extends Component {
if (data.expanded) {
const childs = data.childs.map(child => {
return h(JSONNode, {
path: path + '/' + child.prop,
path: path.concat(child.prop),
data: child,
options,
events
@ -73,13 +74,13 @@ export default class JSONNode extends Component {
return h('li', {}, contents)
}
renderJSONArray ({data, path, index, options, events}) {
renderJSONArray ({path, data, options, events}) {
const childCount = data.childs.length
const contents = [
h('div', {class: 'jsoneditor-node jsoneditor-array'}, [
this.renderExpandButton(),
this.renderContextMenuButton(),
this.renderProperty(data, index, options),
this.renderProperty(path, data, options),
this.renderSeparator(), // TODO: remove separator for Object and Array (gives an issue in Preact)
this.renderReadonly(`[${childCount}]`, `Array containing ${childCount} items`)
])
@ -88,9 +89,8 @@ export default class JSONNode extends Component {
if (data.expanded) {
const childs = data.childs.map((child, index) => {
return h(JSONNode, {
path: path + '/' + index,
path: path.concat(index),
data: child,
index,
options,
events
})
@ -102,12 +102,12 @@ export default class JSONNode extends Component {
return h('li', {}, contents)
}
renderJSONValue ({data, index, options}) {
renderJSONValue ({path, data, options}) {
return h('li', {}, [
h('div', {class: 'jsoneditor-node'}, [
h('div', {class: 'jsoneditor-button-placeholder'}),
this.renderContextMenuButton(),
this.renderProperty(data, index, options),
this.renderProperty(path, data, options),
this.renderSeparator(),
this.renderValue(data.value)
])
@ -118,21 +118,30 @@ export default class JSONNode extends Component {
return h('div', {class: 'jsoneditor-readonly', contentEditable: false, title}, text)
}
renderProperty (data, index, options) {
const isProperty = typeof data.prop === 'string'
const content = isProperty
? escapeHTML(data.prop) // render the property name
: index !== undefined
? index // render the array index of the item
: JSONNode._rootName(data, options)
renderProperty (path, data, options) {
if (path.length > 0) {
const content = last(path)
const isIndex = typeof content === 'number'
return h('div', {
class: 'jsoneditor-property' + (isProperty ? '' : ' jsoneditor-readonly'),
contentEditable: isProperty,
class: 'jsoneditor-property' + (isIndex ? ' jsoneditor-readonly' : ''),
contentEditable: !isIndex,
spellCheck: 'false',
onInput: this.handleChangeProperty
}, content)
}
else {
// root node
const content = JSONNode._rootName(data, options)
return h('div', {
class: 'jsoneditor-property jsoneditor-readonly',
contentEditable: false,
spellCheck: 'false',
onInput: this.handleChangeProperty
}, content)
}
}
renderSeparator() {
return h('div', {class: 'jsoneditor-separator'}, ':')
@ -175,7 +184,7 @@ export default class JSONNode extends Component {
renderContextMenu ({anchor, root}) {
const path = this.props.path
const hasParent = path !== ''
const hasParent = path.length > 0
const type = this.props.data.type
const events = this.props.events
const items = [] // array with menu items
@ -321,10 +330,9 @@ export default class JSONNode extends Component {
const newProp = unescapeHTML(getInnerText(event.target))
// remove last entry from the path to get the path of the parent object
const index = this.props.path.lastIndexOf('/')
const path = this.props.path.substr(0, index)
const parentPath = this.props.path.slice(0, this.props.path.length - 1)
this.props.events.onChangeProperty(path, oldProp, newProp)
this.props.events.onChangeProperty(parentPath, oldProp, newProp)
}
handleChangeValue (event) {

View File

@ -2,7 +2,7 @@ import { h, Component } from 'preact'
import * as pointer from 'json-pointer'
import { setIn, updateIn, getIn, deleteIn, cloneDeep } from './utils/objectUtils'
import { compareAsc, compareDesc } from './utils/arrayUtils'
import { compareAsc, compareDesc, last } from './utils/arrayUtils'
import { isObject } from './utils/typeUtils'
import bindMethods from './utils/bindMethods'
import JSONNode from './JSONNode'
@ -23,7 +23,6 @@ export default class Main extends Component {
data: {
type: 'object',
expanded: true,
path: [],
childs: []
},
@ -56,7 +55,7 @@ export default class Main extends Component {
data: this.state.data,
events: this.state.events,
options: this.state.options,
path: ''
path: []
})
])
])
@ -72,7 +71,7 @@ export default class Main extends Component {
console.log('handleChangeProperty', path, oldProp, newProp)
const index = this._findIndex(path, oldProp)
const newPath = path + '/' + pointer.escape(newProp)
const newPath = path.concat(newProp)
this._setIn(path, ['childs', index, 'path'], newPath)
this._setIn(path, ['childs', index, 'prop'], newProp)
@ -91,11 +90,8 @@ export default class Main extends Component {
// TODO: this method is quite complicated. Can we simplify it?
const parsedPath = pointer.parse(path)
const afterProp = parsedPath[parsedPath.length - 1]
const parentPath = parsedPath.slice(0, parsedPath.length - 1)
.map(entry => '/' + pointer.escape(entry)).join('')
const afterProp = last(path)
const parentPath = path.slice(0, path.length - 1)
const parent = this._getIn(parentPath)
const index = parent.type === 'array'
@ -126,11 +122,8 @@ export default class Main extends Component {
// TODO: this method is quite complicated. Can we simplify it?
const parsedPath = pointer.parse(path)
const prop = parsedPath[parsedPath.length - 1]
const parentPath = parsedPath.slice(0, parsedPath.length - 1)
.map(entry => '/' + pointer.escape(entry)).join('')
const prop = last(path)
const parentPath = path.slice(0, path.length - 1)
const parent = this._getIn(parentPath)
const index = parent.type === 'array'
@ -157,8 +150,8 @@ export default class Main extends Component {
}
/**
*
* @param path
* Order the childs of an array in ascending or descending order
* @param {Array.<string | number>} path
* @param {'asc' | 'desc' | null} [order=null] If not provided, will toggle current ordering
*/
handleSort (path, order = null) {
@ -166,11 +159,6 @@ export default class Main extends Component {
this.handleHideContextMenu() // TODO: should be handled by the contextmenu itself
const comparators = {
asc: compareAsc,
desc: compareDesc
}
let _order
if (order === 'asc' || order === 'desc') {
_order = order
@ -184,7 +172,7 @@ export default class Main extends Component {
this._updateIn(path, ['childs'], function (childs) {
const ordered = childs.slice(0)
const compare = comparators[_order] || comparators['asc']
const compare = _order === 'desc' ? compareDesc : compareAsc
ordered.sort((a, b) => compare(a.value, b.value))
@ -199,14 +187,14 @@ export default class Main extends Component {
}
/**
* Set ContextMenu to a json pointer, or hide the context menu by passing null
* @param {string | null} path
* Set ContextMenu to a json pointer, or hide the context menu by passing null as path
* @param {Array.<string | number> | null} path
* @param {Element} anchor
* @param {Element} root
* @private
*/
handleShowContextMenu({path, anchor, root}) {
let data = this.state.data
console.log('handleShowContextMenu', path, anchor, root)
// TODO: remove this cached this.state.contextMenuPath and do a brute-force sweep over the data instead?
// hide previous context menu (if any)
@ -215,12 +203,12 @@ export default class Main extends Component {
}
// show new menu
if (typeof path === 'string') {
if (Array.isArray(path)) {
this._setIn(path, ['contextMenu'], {anchor, root})
}
this.setState({
contextMenuPath: typeof path === 'string' ? path : null // store path of current menu, just to easily find it next time
contextMenuPath: Array.isArray(path) ? path : null // store path of current menu, just to easily find it next time
})
}
@ -230,13 +218,13 @@ export default class Main extends Component {
}
_getIn (path, modelProps = []) {
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path))
const modelPath = Main._pathToModelPath(this.state.data, path)
return getIn(this.state.data, modelPath.concat(modelProps))
}
_setIn (path, modelProps = [], value) {
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path))
const modelPath = Main._pathToModelPath(this.state.data, path)
this.setState({
data: setIn(this.state.data, modelPath.concat(modelProps), value)
@ -244,7 +232,7 @@ export default class Main extends Component {
}
_updateIn (path, modelProps = [], callback) {
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path))
const modelPath = Main._pathToModelPath(this.state.data, path)
this.setState({
data: updateIn(this.state.data, modelPath.concat(modelProps), callback)
@ -252,7 +240,7 @@ export default class Main extends Component {
}
_deleteIn (path, modelProps = []) {
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path))
const modelPath = Main._pathToModelPath(this.state.data, path)
this.setState({
data: deleteIn(this.state.data, modelPath.concat(modelProps))
@ -272,7 +260,7 @@ export default class Main extends Component {
// TODO: comment
set (json) {
this.setState({
data: Main._jsonToModel('', null, json, this.state.options.expand)
data: Main._jsonToModel([], null, json, this.state.options.expand)
})
}
@ -281,25 +269,11 @@ export default class Main extends Component {
*
* Rule: expand the root node only
*
* @param {string} path A JSON Pointer path
* @param {Array.<string | number>} path
* @return {boolean}
*/
static expand (path) {
return path.indexOf('/') === -1
}
/**
* parse json pointer into an array, and replace strings containing a number
* with a number
* @param {string} path
* @return {Array.<string | number>}
* @private
*/
static _parsePath (path) {
return pointer.parse(path).map(item => {
const num = Number(item)
return isNaN(num) ? item : num
})
return path.length === 0
}
/**
@ -330,10 +304,10 @@ export default class Main extends Component {
/**
* Convert a JSON object into the internally used data model
* @param {string} path
* @param {Array.<string | number>} path
* @param {string | null} prop
* @param {Object | Array | string | number | boolean | null} value
* @param {function(path: string)} expand
* @param {function(path: Array.<string | number>)} expand
* @return {Model}
* @private
*/
@ -343,7 +317,7 @@ export default class Main extends Component {
type: 'array',
expanded: expand(path),
prop,
childs: value.map((child, index) => Main._jsonToModel(path + '/' + index, null, child, expand))
childs: value.map((child, index) => Main._jsonToModel(path.concat(index), null, child, expand))
}
}
else if (isObject(value)) {
@ -352,7 +326,7 @@ export default class Main extends Component {
expanded: expand(path),
prop,
childs: Object.keys(value).map(prop => {
return Main._jsonToModel(path + '/' + pointer.escape(prop), prop, value[prop], expand)
return Main._jsonToModel(path.concat(prop), prop, value[prop], expand)
})
}
}