Some refactoring

This commit is contained in:
jos 2017-12-27 17:33:40 +01:00
parent 46661177db
commit 8bb9cb10cc
2 changed files with 70 additions and 107 deletions

View File

@ -4,7 +4,7 @@ import initial from 'lodash/initial'
import FloatingMenu from './menu/FloatingMenu' import FloatingMenu from './menu/FloatingMenu'
import { escapeHTML, unescapeHTML } from '../utils/stringUtils' import { escapeHTML, unescapeHTML } from '../utils/stringUtils'
import { getInnerText, insideRect, findParentWithAttribute } from '../utils/domUtils' import { getInnerText, insideRect } from '../utils/domUtils'
import { stringConvert, valueType, isUrl } from '../utils/typeUtils' import { stringConvert, valueType, isUrl } from '../utils/typeUtils'
import { compileJSONPointer, META, SELECTED, SELECTED_END, SELECTED_AFTER, SELECTED_BEFORE } from '../eson' import { compileJSONPointer, META, SELECTED, SELECTED_END, SELECTED_AFTER, SELECTED_BEFORE } from '../eson'
@ -29,7 +29,7 @@ export default class JSONNode extends PureComponent {
static propTypes = { static propTypes = {
prop: PropTypes.string, // in case of an object property prop: PropTypes.string, // in case of an object property
index: PropTypes.number, // in case of an array item index: PropTypes.number, // in case of an array item
eson: PropTypes.oneOfType([ PropTypes.object, PropTypes.array ]).isRequired, // TODO: rename "eson" to "value"? value: PropTypes.oneOfType([ PropTypes.object, PropTypes.array ]).isRequired,
emit: PropTypes.func.isRequired, emit: PropTypes.func.isRequired,
findKeyBinding: PropTypes.func.isRequired, findKeyBinding: PropTypes.func.isRequired,
@ -56,11 +56,12 @@ export default class JSONNode extends PureComponent {
} }
render () { render () {
// console.log('JSONNode.render ' + JSON.stringify(this.props.eson[META].path)) // console.log('JSONNode.render ' + JSON.stringify(this.props.value[META].path))
if (this.props.eson[META].type === 'Object') { const type = this.props.value[META].type
if (type === 'Object') {
return this.renderJSONObject() return this.renderJSONObject()
} }
else if (this.props.eson[META].type === 'Array') { else if (type === 'Array') {
return this.renderJSONArray() return this.renderJSONArray()
} }
else { // no Object or Array else { // no Object or Array
@ -69,25 +70,26 @@ export default class JSONNode extends PureComponent {
} }
renderJSONObject () { renderJSONObject () {
const props = this.props.eson[META].props const meta = this.props.value[META]
const props = meta.props
const node = h('div', { const node = h('div', {
key: 'node', key: 'node',
onKeyDown: this.handleKeyDown, onKeyDown: this.handleKeyDown,
className: 'jsoneditor-node jsoneditor-object' className: 'jsoneditor-node jsoneditor-object'
}, [ }, [
this.renderExpandButton(), this.renderExpandButton(),
this.renderProperty(this.props.prop, this.props.index, this.props.eson, this.props.options), this.renderProperty(),
this.renderReadonly(`{${props.length}}`, `Object containing ${props.length} items`), this.renderReadonly(`{${props.length}}`, `Object containing ${props.length} items`),
this.renderError(this.props.eson[META].error) this.renderError(meta.error)
]) ])
let childs let childs
if (this.props.eson[META].expanded) { if (meta.expanded) {
if (props.length > 0) { if (props.length > 0) {
const propsChilds = props.map(prop => h(this.constructor, { const propsChilds = props.map(prop => h(this.constructor, {
key: this.props.eson[prop][META].id, key: this.props.value[prop][META].id,
prop, prop,
eson: this.props.eson[prop], value: this.props.value[prop],
emit: this.props.emit, emit: this.props.emit,
findKeyBinding: this.props.findKeyBinding, findKeyBinding: this.props.findKeyBinding,
options: this.props.options options: this.props.options
@ -102,7 +104,7 @@ export default class JSONNode extends PureComponent {
} }
} }
const floatingMenu = (this.props.eson[META].selected === SELECTED_END) const floatingMenu = (meta.selected === SELECTED_END)
? this.renderFloatingMenu([ ? this.renderFloatingMenu([
{type: 'sort'}, {type: 'sort'},
{type: 'duplicate'}, {type: 'duplicate'},
@ -116,32 +118,33 @@ export default class JSONNode extends PureComponent {
const insertArea = this.renderInsertBeforeArea() const insertArea = this.renderInsertBeforeArea()
return h('div', { return h('div', {
'data-path': compileJSONPointer(this.props.eson[META].path), 'data-path': compileJSONPointer(meta.path),
className: this.getContainerClassName(this.props.eson[META].selected, this.state.hover), className: this.getContainerClassName(meta.selected, this.state.hover),
onMouseOver: this.handleMouseOver, onMouseOver: this.handleMouseOver,
onMouseLeave: this.handleMouseLeave onMouseLeave: this.handleMouseLeave
}, [node, floatingMenu, insertArea, childs]) }, [node, floatingMenu, insertArea, childs])
} }
renderJSONArray () { renderJSONArray () {
const meta = this.props.value[META]
const node = h('div', { const node = h('div', {
key: 'node', key: 'node',
onKeyDown: this.handleKeyDown, onKeyDown: this.handleKeyDown,
className: 'jsoneditor-node jsoneditor-array' className: 'jsoneditor-node jsoneditor-array'
}, [ }, [
this.renderExpandButton(), this.renderExpandButton(),
this.renderProperty(this.props.prop, this.props.index, this.props.eson, this.props.options), this.renderProperty(),
this.renderReadonly(`[${this.props.eson.length}]`, `Array containing ${this.props.eson.length} items`), this.renderReadonly(`[${this.props.value.length}]`, `Array containing ${this.props.value.length} items`),
this.renderError(this.props.eson[META].error) this.renderError(meta.error)
]) ])
let childs let childs
if (this.props.eson[META].expanded) { if (meta.expanded) {
if (this.props.eson.length > 0) { if (this.props.value.length > 0) {
const items = this.props.eson.map((item, index) => h(this.constructor, { const items = this.props.value.map((item, index) => h(this.constructor, {
key : item[META].id, key : item[META].id,
index, index,
eson: item, value: item,
options: this.props.options, options: this.props.options,
emit: this.props.emit, emit: this.props.emit,
findKeyBinding: this.props.findKeyBinding findKeyBinding: this.props.findKeyBinding
@ -156,7 +159,7 @@ export default class JSONNode extends PureComponent {
} }
} }
const floatingMenu = (this.props.eson[META].selected === SELECTED_END) const floatingMenu = (meta.selected === SELECTED_END)
? this.renderFloatingMenu([ ? this.renderFloatingMenu([
{type: 'sort'}, {type: 'sort'},
{type: 'duplicate'}, {type: 'duplicate'},
@ -170,30 +173,28 @@ export default class JSONNode extends PureComponent {
const insertArea = this.renderInsertBeforeArea() const insertArea = this.renderInsertBeforeArea()
return h('div', { return h('div', {
'data-path': compileJSONPointer(this.props.eson[META].path), 'data-path': compileJSONPointer(meta.path),
className: this.getContainerClassName(this.props.eson[META].selected, this.state.hover), className: this.getContainerClassName(meta.selected, this.state.hover),
onMouseOver: this.handleMouseOver, onMouseOver: this.handleMouseOver,
onMouseLeave: this.handleMouseLeave onMouseLeave: this.handleMouseLeave
}, [node, floatingMenu, insertArea, childs]) }, [node, floatingMenu, insertArea, childs])
} }
renderJSONValue () { renderJSONValue () {
const meta = this.props.value[META]
const node = h('div', { const node = h('div', {
key: 'node', key: 'node',
onKeyDown: this.handleKeyDown, onKeyDown: this.handleKeyDown,
className: 'jsoneditor-node' className: 'jsoneditor-node'
}, [ }, [
this.renderPlaceholder(), this.renderPlaceholder(),
// this.renderActionMenu('update', this.state.menu, this.handleCloseActionMenu), this.renderProperty(),
// this.renderActionMenuButton(),
this.renderProperty(this.props.prop, this.props.index, this.props.eson, this.props.options),
this.renderSeparator(), this.renderSeparator(),
this.renderValue(this.props.eson[META].value, this.props.eson[META].searchValue, this.props.options), this.renderValue(meta.value, meta.searchValue, this.props.options),
// this.renderFloatingMenuButton(), this.renderError(meta.error)
this.renderError(this.props.eson[META].error)
]) ])
const floatingMenu = (this.props.eson[META].selected === SELECTED_END) const floatingMenu = (meta.selected === SELECTED_END)
? this.renderFloatingMenu([ ? this.renderFloatingMenu([
// {text: 'String', onClick: this.props.emit('changeType', {type: 'checkbox', checked: false}}), // {text: 'String', onClick: this.props.emit('changeType', {type: 'checkbox', checked: false}}),
{type: 'duplicate'}, {type: 'duplicate'},
@ -207,15 +208,15 @@ export default class JSONNode extends PureComponent {
const insertArea = this.renderInsertBeforeArea() const insertArea = this.renderInsertBeforeArea()
return h('div', { return h('div', {
'data-path': compileJSONPointer(this.props.eson[META].path), 'data-path': compileJSONPointer(meta.path),
className: this.getContainerClassName(this.props.eson[META].selected, this.state.hover), className: this.getContainerClassName(meta.selected, this.state.hover),
onMouseOver: this.handleMouseOver, onMouseOver: this.handleMouseOver,
onMouseLeave: this.handleMouseLeave onMouseLeave: this.handleMouseLeave
}, [node, floatingMenu, insertArea]) }, [node, floatingMenu, insertArea])
} }
renderInsertBeforeArea () { renderInsertBeforeArea () {
const floatingMenu = (this.props.eson[META].selected === SELECTED_BEFORE) const floatingMenu = (this.props.value[META].selected === SELECTED_BEFORE)
? this.renderFloatingMenu([ ? this.renderFloatingMenu([
{type: 'insertStructure'}, {type: 'insertStructure'},
{type: 'insertValue'}, {type: 'insertValue'},
@ -239,13 +240,11 @@ export default class JSONNode extends PureComponent {
*/ */
renderAppend (text) { renderAppend (text) {
return h('div', { return h('div', {
'data-path': compileJSONPointer(this.props.eson[META].path) + '/-', 'data-path': compileJSONPointer(this.props.value[META].path) + '/-',
className: 'jsoneditor-node', className: 'jsoneditor-node',
onKeyDown: this.handleKeyDownAppend onKeyDown: this.handleKeyDownAppend
}, [ }, [
this.renderPlaceholder(), this.renderPlaceholder(),
// this.renderActionMenu('append', this.state.appendMenu, this.handleCloseAppendActionMenu),
// this.renderAppendActionMenuButton(),
this.renderReadonly(text) this.renderReadonly(text)
]) ])
} }
@ -262,18 +261,14 @@ export default class JSONNode extends PureComponent {
/** /**
* Render a property field of a JSONNode * Render a property field of a JSONNode
* @param {string} [prop]
* @param {number} [index]
* @param {ESON} eson
* @param {{escapeUnicode: boolean, isPropertyEditable: function(Path) : boolean}} options
*/ */
renderProperty (prop, index, eson, options) { renderProperty () {
const isIndex = typeof index === 'number' const isIndex = typeof this.props.index === 'number'
const isProp = typeof prop === 'string' const isProp = typeof this.props.prop === 'string'
if (!isProp && !isIndex) { if (!isProp && !isIndex) {
// root node // root node
const rootName = JSONNode.getRootName(eson, options) const rootName = JSONNode.getRootName(this.props.value, this.props.options)
return h('div', { return h('div', {
key: 'property', key: 'property',
@ -283,11 +278,11 @@ export default class JSONNode extends PureComponent {
}, rootName) }, rootName)
} }
const editable = !isIndex && (!options.isPropertyEditable || options.isPropertyEditable(this.props.eson[META].path)) const editable = !isIndex && (!this.props.options.isPropertyEditable || this.props.options.isPropertyEditable(this.props.value[META].path))
const emptyClassName = (prop != null && prop.length === 0) ? ' jsoneditor-empty' : '' const emptyClassName = (this.props.prop != null && this.props.prop.length === 0) ? ' jsoneditor-empty' : ''
const searchClassName = prop != null ? JSONNode.getSearchResultClass(eson[META].searchProperty) : '' const searchClassName = this.props.prop != null ? JSONNode.getSearchResultClass(this.props.value[META].searchProperty) : ''
const escapedPropName = prop != null ? escapeHTML(prop, options.escapeUnicode) : null const escapedPropName = this.props.prop != null ? escapeHTML(this.props.prop, this.props.options.escapeUnicode) : null
if (editable) { if (editable) {
return h('div', { return h('div', {
@ -304,7 +299,7 @@ export default class JSONNode extends PureComponent {
key: 'property', key: 'property',
className: 'jsoneditor-property jsoneditor-readonly' + searchClassName, className: 'jsoneditor-property jsoneditor-readonly' + searchClassName,
spellCheck: 'false' spellCheck: 'false'
}, isIndex ? index : escapedPropName) }, isIndex ? this.props.index : escapedPropName)
} }
} }
@ -318,7 +313,7 @@ export default class JSONNode extends PureComponent {
const itsAnUrl = isUrl(value) const itsAnUrl = isUrl(value)
const isEmpty = escapedValue.length === 0 const isEmpty = escapedValue.length === 0
const editable = !options.isValueEditable || options.isValueEditable(this.props.eson[META].path) const editable = !options.isValueEditable || options.isValueEditable(this.props.value[META].path)
if (editable) { if (editable) {
return h('div', { return h('div', {
key: 'value', key: 'value',
@ -411,7 +406,7 @@ export default class JSONNode extends PureComponent {
} }
target.className = JSONNode.getValueClass(type, itsAnUrl, isEmpty) + target.className = JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
JSONNode.getSearchResultClass(this.props.eson[META].searchValue) JSONNode.getSearchResultClass(this.props.value[META].searchValue)
target.title = itsAnUrl ? JSONNode.URL_TITLE : '' target.title = itsAnUrl ? JSONNode.URL_TITLE : ''
// remove all classNames from childs (needed for IE and Edge) // remove all classNames from childs (needed for IE and Edge)
@ -465,7 +460,7 @@ export default class JSONNode extends PureComponent {
} }
renderExpandButton () { renderExpandButton () {
const className = `jsoneditor-button jsoneditor-${this.props.eson[META].expanded ? 'expanded' : 'collapsed'}` const className = `jsoneditor-button jsoneditor-${this.props.value[META].expanded ? 'expanded' : 'collapsed'}`
return h('div', {key: 'expand', className: 'jsoneditor-button-container'}, return h('div', {key: 'expand', className: 'jsoneditor-button-container'},
h('button', { h('button', {
@ -481,7 +476,7 @@ export default class JSONNode extends PureComponent {
renderFloatingMenu (items) { renderFloatingMenu (items) {
return h(FloatingMenu, { return h(FloatingMenu, {
key: 'floating-menu', key: 'floating-menu',
path: this.props.eson[META].path, path: this.props.value[META].path,
emit: this.props.emit, emit: this.props.emit,
items items
}) })
@ -515,49 +510,15 @@ export default class JSONNode extends PureComponent {
this.setState({hover: null}) this.setState({hover: null})
} }
handleOpenActionMenu = (event) => { static getRootName (value, options) {
// TODO: don't use refs, find root purely via DOM?
const root = findParentWithAttribute(this.refs.actionMenuButton, 'data-jsoneditor', 'true')
this.setState({
menu: {
open: true,
anchor: this.refs.actionMenuButton,
root
}
})
}
handleCloseActionMenu = () => {
this.setState({ menu: null })
}
handleOpenAppendActionMenu = (event) => {
// TODO: don't use refs, find root purely via DOM?
const root = findParentWithAttribute(this.refs.appendActionMenuButton, 'data-jsoneditor', 'true')
this.setState({
appendMenu: {
open: true,
anchor: this.refs.actionMenuButton,
root
}
})
}
handleCloseAppendActionMenu = () => {
this.setState({ appendMenu: null })
}
static getRootName (eson, options) {
return typeof options.name === 'string' return typeof options.name === 'string'
? options.name ? options.name
: valueType(eson) : value[META].type
} }
/** @private */ /** @private */
handleChangeProperty = (event) => { handleChangeProperty = (event) => {
const parentPath = initial(this.props.eson[META].path) const parentPath = initial(this.props.value[META].path)
const oldProp = this.props.prop const oldProp = this.props.prop
const newProp = unescapeHTML(getInnerText(event.target)) const newProp = unescapeHTML(getInnerText(event.target))
@ -569,9 +530,9 @@ export default class JSONNode extends PureComponent {
/** @private */ /** @private */
handleChangeValue = (event) => { handleChangeValue = (event) => {
const value = this.getValueFromEvent(event) const value = this.getValueFromEvent(event)
const path = this.props.eson[META].path const path = this.props.value[META].path
if (value !== this.props.eson[META].value) { if (value !== this.props.value[META].value) {
this.props.emit('changeValue', {path, value}) this.props.emit('changeValue', {path, value})
} }
} }
@ -586,47 +547,49 @@ export default class JSONNode extends PureComponent {
/** @private */ /** @private */
handleKeyDown = (event) => { handleKeyDown = (event) => {
const keyBinding = this.props.findKeyBinding(event) const keyBinding = this.props.findKeyBinding(event)
const path = this.props.value[META].path
if (keyBinding === 'duplicate') { if (keyBinding === 'duplicate') {
event.preventDefault() event.preventDefault()
this.props.emit('duplicate', {path: this.props.eson[META].path}) this.props.emit('duplicate', {path})
} }
if (keyBinding === 'insert') { if (keyBinding === 'insert') {
event.preventDefault() event.preventDefault()
this.props.emit('insert', {path: this.props.eson[META].path, type: 'value'}) this.props.emit('insert', {path, type: 'value'})
} }
if (keyBinding === 'remove') { if (keyBinding === 'remove') {
event.preventDefault() event.preventDefault()
this.props.emit('remove', {path: this.props.eson[META].path}) this.props.emit('remove', {path})
} }
if (keyBinding === 'expand') { if (keyBinding === 'expand') {
event.preventDefault() event.preventDefault()
const recurse = false const recurse = false
const expanded = !this.props.eson[META].expanded const expanded = !this.props.value[META].expanded
this.props.emit('expand', {path: this.props.eson[META].path, expanded, recurse}) this.props.emit('expand', {path, expanded, recurse})
} }
if (keyBinding === 'actionMenu') { if (keyBinding === 'actionMenu') {
event.preventDefault() event.preventDefault()
this.handleOpenActionMenu(event) // FIXME: open floating menu
} }
} }
/** @private */ /** @private */
handleKeyDownAppend = (event) => { handleKeyDownAppend = (event) => {
const keyBinding = this.props.findKeyBinding(event) const keyBinding = this.props.findKeyBinding(event)
const path = this.props.value[META].path
if (keyBinding === 'insert') { if (keyBinding === 'insert') {
event.preventDefault() event.preventDefault()
this.props.emit('append', {path: this.props.eson[META].path, type: 'value'}) this.props.emit('append', {path, type: 'value'})
} }
if (keyBinding === 'actionMenu') { if (keyBinding === 'actionMenu') {
event.preventDefault() event.preventDefault()
this.handleOpenAppendActionMenu(event) // FIXME: open floating menu
} }
} }
@ -642,8 +605,8 @@ export default class JSONNode extends PureComponent {
/** @private */ /** @private */
handleExpand = (event) => { handleExpand = (event) => {
const recurse = event.ctrlKey const recurse = event.ctrlKey
const path = this.props.eson[META].path const path = this.props.value[META].path
const expanded = !this.props.eson[META].expanded const expanded = !this.props.value[META].expanded
this.props.emit('expand', {path, expanded, recurse}) this.props.emit('expand', {path, expanded, recurse})
} }
@ -672,7 +635,7 @@ export default class JSONNode extends PureComponent {
*/ */
getValueFromEvent (event) { getValueFromEvent (event) {
const stringValue = unescapeHTML(getInnerText(event.target)) const stringValue = unescapeHTML(getInnerText(event.target))
return this.props.eson[META].type === 'string' return this.props.value[META].type === 'string'
? stringValue ? stringValue
: stringConvert(stringValue) : stringConvert(stringValue)
} }

View File

@ -233,7 +233,7 @@ export default class TreeMode extends PureComponent {
className: 'jsoneditor-list jsoneditor-root' + className: 'jsoneditor-list jsoneditor-root' +
(eson[META].selected ? ' jsoneditor-selected' : '')}, (eson[META].selected ? ' jsoneditor-selected' : '')},
h(Node, { h(Node, {
eson, value: eson,
emit: this.emitter.emit, emit: this.emitter.emit,
findKeyBinding: this.findKeyBinding, findKeyBinding: this.findKeyBinding,
options: this.state.options options: this.state.options
@ -850,7 +850,7 @@ export default class TreeMode extends PureComponent {
historyIndex: historyIndex + 1 historyIndex: historyIndex + 1
}) })
this.emitOnChange(historyItem.undo, historyItem.redo, result.eson, result.json) this.emitOnChange(historyItem.undo, historyItem.redo, result.data, esonToJson(result.data))
} }
} }
@ -869,7 +869,7 @@ export default class TreeMode extends PureComponent {
historyIndex historyIndex
}) })
this.emitOnChange(historyItem.redo, historyItem.undo, result.eson, result.json) this.emitOnChange(historyItem.redo, historyItem.undo, result.data, esonToJson(result.data))
} }
} }