Largely fixed jumping of fields when renaming. Some refactoring.
This commit is contained in:
parent
8723855bdf
commit
c0f075a9e6
|
@ -31,7 +31,6 @@ ul.jsoneditor-list {
|
|||
.jsoneditor-value,
|
||||
.jsoneditor-readonly {
|
||||
min-width: 24px;
|
||||
border-radius: 2px;
|
||||
word-break: break-word;
|
||||
|
||||
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;
|
||||
|
@ -40,6 +39,11 @@ ul.jsoneditor-list {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.jsoneditor-field,
|
||||
.jsoneditor-value {
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.jsoneditor-field:focus,
|
||||
.jsoneditor-value:focus {
|
||||
box-shadow: 0 0 3px 1px #008fd5;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { h, Component } from 'preact'
|
||||
import isObject from './utils/isObject'
|
||||
import { isObject } from './utils/objectUtils'
|
||||
import { escapeHTML, unescapeHTML } from './utils/escape'
|
||||
import getInnerText from './utils/getInnerText'
|
||||
import stringConvert from './utils/stringConvert'
|
||||
|
@ -99,7 +99,7 @@ export default class JSONNode extends Component {
|
|||
class: 'jsoneditor-field' + (hasParent ? '' : ' jsoneditor-readonly'),
|
||||
contentEditable: hasParent,
|
||||
spellCheck: 'false',
|
||||
onBlur: onChangeField
|
||||
onInput: onChangeField
|
||||
}, content)
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ export default class JSONNode extends Component {
|
|||
const newField = unescapeHTML(getInnerText(event.target))
|
||||
const oldField = this.props.field
|
||||
if (newField !== oldField) {
|
||||
this.props.onChangeField(path, newField, oldField)
|
||||
this.props.onChangeField(path, oldField, newField)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
19
src/Main.js
19
src/Main.js
|
@ -1,7 +1,6 @@
|
|||
import { h, Component } from 'preact'
|
||||
import setIn from './utils/setIn'
|
||||
import getIn from './utils/getIn'
|
||||
import clone from './utils/clone'
|
||||
|
||||
import { getIn, setIn, renameField } from './utils/objectUtils'
|
||||
import JSONNode from './JSONNode'
|
||||
|
||||
export default class Main extends Component {
|
||||
|
@ -37,18 +36,14 @@ export default class Main extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
onChangeField (path, newField, oldField) {
|
||||
onChangeField (path, oldField, newField) {
|
||||
console.log('onChangeField', path, newField, oldField)
|
||||
|
||||
const oldObject = getIn(this.state.json, path)
|
||||
const newObject = renameField(oldObject, oldField, newField)
|
||||
|
||||
const value = clone(getIn(this.state.json, path))
|
||||
|
||||
console.log('value', value)
|
||||
|
||||
value[newField] = value[oldField]
|
||||
delete value[oldField]
|
||||
|
||||
this.setState({
|
||||
json: setIn(this.state.json, path, value)
|
||||
json: setIn(this.state.json, path, newObject)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import isObject from './isObject'
|
||||
|
||||
// TODO: unit test clone
|
||||
|
||||
/**
|
||||
* Flat clone the properties of an object or array
|
||||
* @param {Object | Array} value
|
||||
* @return {Object | Array} Returns a flat clone of the object or Array
|
||||
*/
|
||||
export default function clone (value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.slice(0)
|
||||
}
|
||||
else if (isObject(value)) {
|
||||
const cloned = {}
|
||||
|
||||
Object.keys(value).forEach(key => {
|
||||
cloned[key] = value[key]
|
||||
})
|
||||
|
||||
return cloned
|
||||
}
|
||||
else {
|
||||
// a primitive value
|
||||
return value
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
import isObject from './isObject'
|
||||
|
||||
// TODO: unit test getIn
|
||||
|
||||
/**
|
||||
* helper function to get a nested property in an object or array
|
||||
*
|
||||
* @param {Object | Array} object
|
||||
* @param {Array.<string | number>} path
|
||||
* @return {* | undefined} Returns the field when found, or undefined when the
|
||||
* path doesn't exist
|
||||
*/
|
||||
export default function getIn (object, path) {
|
||||
let value = object
|
||||
let i = 0
|
||||
|
||||
while(i < path.length) {
|
||||
if (Array.isArray(value) || isObject(value)) {
|
||||
value = value[path[i]]
|
||||
}
|
||||
else {
|
||||
value = undefined
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
window.getIn = getIn // TODO: cleanup
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Test whether a value is an object (and not an Array or Date or primitive value)
|
||||
*
|
||||
* @param {*} value
|
||||
* @return {boolean}
|
||||
*/
|
||||
export default function isObject (value) {
|
||||
return typeof value === 'object' &&
|
||||
value && // not null
|
||||
!Array.isArray(value) &&
|
||||
value.toString() === '[object Object]'
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
|
||||
/**
|
||||
* Test whether a value is an object (and not an Array or Date or primitive value)
|
||||
*
|
||||
* @param {*} value
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function isObject (value) {
|
||||
return typeof value === 'object' &&
|
||||
value && // not null
|
||||
!Array.isArray(value) &&
|
||||
value.toString() === '[object Object]'
|
||||
}
|
||||
|
||||
// TODO: unit test getIn
|
||||
|
||||
/**
|
||||
* Flat clone the properties of an object or array
|
||||
* @param {Object | Array} value
|
||||
* @return {Object | Array} Returns a flat clone of the object or Array
|
||||
*/
|
||||
export function clone (value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.slice(0)
|
||||
}
|
||||
else if (isObject(value)) {
|
||||
const cloned = {}
|
||||
|
||||
Object.keys(value).forEach(key => {
|
||||
cloned[key] = value[key]
|
||||
})
|
||||
|
||||
return cloned
|
||||
}
|
||||
else {
|
||||
// a primitive value
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function to get a nested property in an object or array
|
||||
*
|
||||
* @param {Object | Array} object
|
||||
* @param {Array.<string | number>} path
|
||||
* @return {* | undefined} Returns the field when found, or undefined when the
|
||||
* path doesn't exist
|
||||
*/
|
||||
export function getIn (object, path) {
|
||||
let value = object
|
||||
let i = 0
|
||||
|
||||
while(i < path.length) {
|
||||
if (Array.isArray(value) || isObject(value)) {
|
||||
value = value[path[i]]
|
||||
}
|
||||
else {
|
||||
value = undefined
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// TODO: unit test setIn
|
||||
|
||||
/**
|
||||
* helper function to replace a nested property in an object with a new value
|
||||
* without mutating the object itself.
|
||||
*
|
||||
* @param {Object | Array} object
|
||||
* @param {Array.<string | number>} path
|
||||
* @param {*} value
|
||||
* @return {Object | Array} Returns a new, updated object or array
|
||||
*/
|
||||
export function setIn (object, path, value) {
|
||||
if (path.length === 0) {
|
||||
return value
|
||||
}
|
||||
|
||||
// TODO: change array into object and vice versa when key is a number/string
|
||||
|
||||
const key = path[0]
|
||||
const child = (Array.isArray(object[key]) || isObject(object[key]))
|
||||
? object[key]
|
||||
: (typeof path[1] === 'number' ? [] : {})
|
||||
const updated = clone(object)
|
||||
|
||||
updated[key] = setIn(child, path.slice(1), value)
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a field in an object without mutating the object itself.
|
||||
* The order of the fields in the object is maintained.
|
||||
* @param {Object} object
|
||||
* @param {string} oldField
|
||||
* @param {string} newField
|
||||
* @return {Object} Returns a clone of the object where property `oldField` is
|
||||
* renamed to `newField`
|
||||
*/
|
||||
export function renameField(object, oldField, newField) {
|
||||
const renamed = {}
|
||||
|
||||
// important: maintain the order in which we add the properties to newValue,
|
||||
// else the field will "jump" to another place
|
||||
Object.keys(object).forEach(field => {
|
||||
if (field === oldField) {
|
||||
renamed[newField] = object[oldField]
|
||||
}
|
||||
else {
|
||||
renamed[field] = object[field]
|
||||
}
|
||||
})
|
||||
|
||||
return renamed
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import isObject from './isObject'
|
||||
import clone from './clone'
|
||||
|
||||
// TODO: unit test setIn
|
||||
|
||||
/**
|
||||
* helper function to replace a nested property in an object with a new value
|
||||
* without mutating the object itself.
|
||||
*
|
||||
* @param {Object | Array} object
|
||||
* @param {Array.<string | number>} path
|
||||
* @param {*} value
|
||||
* @return {Object | Array} Returns a new, updated object or array
|
||||
*/
|
||||
export default function setIn (object, path, value) {
|
||||
if (path.length === 0) {
|
||||
return value
|
||||
}
|
||||
|
||||
// TODO: change array into object and vice versa when key is a number/string
|
||||
|
||||
const key = path[0]
|
||||
const child = (Array.isArray(object[key]) || isObject(object[key]))
|
||||
? object[key]
|
||||
: (typeof path[1] === 'number' ? [] : {})
|
||||
const updated = clone(object)
|
||||
|
||||
updated[key] = setIn(child, path.slice(1), value)
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
|
||||
window.setIn = setIn // TODO: cleanup
|
Loading…
Reference in New Issue