Finished switch to React
This commit is contained in:
parent
4208dff7b9
commit
98f56efc47
|
@ -1,4 +1,4 @@
|
|||
import { h, Component } from 'preact'
|
||||
import { createElement as h, Component } from 'react'
|
||||
import ace from '../assets/ace'
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,7 @@ export default class Ace extends Component {
|
|||
}
|
||||
|
||||
render (props, state) {
|
||||
return h('div', {id: this.id, class: 'jsoneditor-code'})
|
||||
return h('div', {ref: 'container', className: 'jsoneditor-code'})
|
||||
}
|
||||
|
||||
shouldComponentUpdate () {
|
||||
|
@ -28,7 +28,7 @@ export default class Ace extends Component {
|
|||
}
|
||||
|
||||
componentDidMount () {
|
||||
const container = this.base
|
||||
const container = this.refs.container
|
||||
|
||||
// use ace from bundle, and if not available
|
||||
// try to use from options or else from global
|
||||
|
|
|
@ -37,11 +37,10 @@ export default class CodeMode extends TextMode {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { props, state } = this
|
||||
return h('div', {className: 'jsoneditor jsoneditor-mode-code'}, [
|
||||
this.renderMenu(),
|
||||
|
||||
h('div', {className: 'jsoneditor-contents'}, h(Ace, {
|
||||
h('div', {key: 'contents', className: 'jsoneditor-contents'}, h(Ace, {
|
||||
value: this.state.text,
|
||||
onChange: this.handleChange,
|
||||
onLoadAce: this.props.options.onLoadAce,
|
||||
|
|
|
@ -41,7 +41,7 @@ export default class JSONNode extends Component {
|
|||
renderJSONObject ({prop, data, options, events}) {
|
||||
const childCount = data.props.length
|
||||
const contents = [
|
||||
h('div', {className: 'jsoneditor-node jsoneditor-object'}, [
|
||||
h('div', {key: 'node', className: 'jsoneditor-node jsoneditor-object'}, [
|
||||
this.renderExpandButton(),
|
||||
this.renderActionMenuButton(),
|
||||
this.renderProperty(prop, data, options),
|
||||
|
@ -66,9 +66,9 @@ export default class JSONNode extends Component {
|
|||
contents.push(h('ul', {key: 'props', className: 'jsoneditor-list'}, props))
|
||||
}
|
||||
else {
|
||||
contents.push(h('ul', {key: 'append', className: 'jsoneditor-list'}, [
|
||||
contents.push(h('ul', {key: 'append', className: 'jsoneditor-list'},
|
||||
this.renderAppend('(empty object)')
|
||||
]))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ export default class JSONNode extends Component {
|
|||
renderJSONArray ({prop, data, options, events}) {
|
||||
const childCount = data.items.length
|
||||
const contents = [
|
||||
h('div', {className: 'jsoneditor-node jsoneditor-array'}, [
|
||||
h('div', {key: 'node', className: 'jsoneditor-node jsoneditor-array'}, [
|
||||
this.renderExpandButton(),
|
||||
this.renderActionMenuButton(),
|
||||
this.renderProperty(prop, data, options),
|
||||
|
@ -102,9 +102,9 @@ export default class JSONNode extends Component {
|
|||
contents.push(h('ul', {key: 'items', className: 'jsoneditor-list'}, items))
|
||||
}
|
||||
else {
|
||||
contents.push(h('ul', {key: 'append', className: 'jsoneditor-list'}, [
|
||||
contents.push(h('ul', {key: 'append', className: 'jsoneditor-list'},
|
||||
this.renderAppend('(empty array)')
|
||||
]))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
|
||||
renderJSONValue ({prop, data, options}) {
|
||||
return h('li', {}, [
|
||||
return h('li', {},
|
||||
h('div', {className: 'jsoneditor-node'}, [
|
||||
this.renderPlaceholder(),
|
||||
this.renderActionMenuButton(),
|
||||
|
@ -121,7 +121,7 @@ export default class JSONNode extends Component {
|
|||
this.renderValue(data.value, data.searchValue, options),
|
||||
this.renderError(data.error)
|
||||
])
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,21 +130,21 @@ export default class JSONNode extends Component {
|
|||
* @return {*}
|
||||
*/
|
||||
renderAppend (text) {
|
||||
return h('li', {key: 'append'}, [
|
||||
return h('li', {key: 'append'},
|
||||
h('div', {className: 'jsoneditor-node'}, [
|
||||
this.renderPlaceholder(),
|
||||
this.renderAppendMenuButton(),
|
||||
this.renderReadonly(text)
|
||||
])
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
renderPlaceholder () {
|
||||
return h('div', {className: 'jsoneditor-button-placeholder'})
|
||||
return h('div', {key: 'placeholder', className: 'jsoneditor-button-placeholder'})
|
||||
}
|
||||
|
||||
renderReadonly (text, title = null) {
|
||||
return h('div', {className: 'jsoneditor-readonly', title}, text)
|
||||
return h('div', {key: 'readonly', className: 'jsoneditor-readonly', title}, text)
|
||||
}
|
||||
|
||||
renderProperty (prop, data, options) {
|
||||
|
@ -153,6 +153,7 @@ export default class JSONNode extends Component {
|
|||
const rootName = JSONNode.getRootName(data, options)
|
||||
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
ref: 'property',
|
||||
className: 'jsoneditor-property jsoneditor-readonly',
|
||||
spellCheck: 'false',
|
||||
|
@ -170,6 +171,7 @@ export default class JSONNode extends Component {
|
|||
const escapedProp = escapeHTML(prop, options.escapeUnicode)
|
||||
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property' + emptyClassName + searchClassName,
|
||||
contentEditable: 'true',
|
||||
spellCheck: 'false',
|
||||
|
@ -178,6 +180,7 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
else {
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property jsoneditor-readonly' + searchClassName,
|
||||
spellCheck: 'false'
|
||||
}, prop)
|
||||
|
@ -185,7 +188,7 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
|
||||
renderSeparator() {
|
||||
return h('div', {className: 'jsoneditor-separator'}, ':')
|
||||
return h('div', {key: 'separator', className: 'jsoneditor-separator'}, ':')
|
||||
}
|
||||
|
||||
renderValue (value, searchValue, options) {
|
||||
|
@ -197,6 +200,7 @@ export default class JSONNode extends Component {
|
|||
const editable = !options.isValueEditable || options.isValueEditable(this.getPath())
|
||||
if (editable) {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
ref: 'value',
|
||||
className: JSONNode.getValueClass(type, itsAnUrl, isEmpty, searchValue),
|
||||
contentEditable: 'true',
|
||||
|
@ -210,6 +214,7 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
else {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
className: 'jsoneditor-readonly',
|
||||
title: itsAnUrl ? JSONNode.URL_TITLE : null
|
||||
}, escapedValue)
|
||||
|
@ -219,12 +224,14 @@ export default class JSONNode extends Component {
|
|||
renderError (error) {
|
||||
if (error) {
|
||||
return h('button', {
|
||||
type: 'button',
|
||||
class: 'jsoneditor-schema-error',
|
||||
onFocus: this.updatePopoverDirection,
|
||||
onMouseOver: this.updatePopoverDirection
|
||||
},
|
||||
h('div', {class: 'jsoneditor-popover jsoneditor-right'}, error.message)
|
||||
key: 'error',
|
||||
ref: 'error',
|
||||
type: 'button',
|
||||
className: 'jsoneditor-schema-error',
|
||||
onFocus: this.updatePopoverDirection,
|
||||
onMouseOver: this.updatePopoverDirection
|
||||
},
|
||||
h('div', {className: 'jsoneditor-popover jsoneditor-right'}, error.message)
|
||||
)
|
||||
}
|
||||
else {
|
||||
|
@ -247,7 +254,8 @@ export default class JSONNode extends Component {
|
|||
popover.className = 'jsoneditor-popover jsoneditor-' + direction
|
||||
|
||||
// FIXME: the contentRect is that of the whole contents, not the visible window
|
||||
const contents = this.base.parentNode.parentNode
|
||||
// TODO: use a ref on the root of the node instead of this parentNode chain?
|
||||
const contents = this.refs.error.parentNode.parentNode.parentNode
|
||||
const contentRect = contents.getBoundingClientRect()
|
||||
const popoverRect = popover.getBoundingClientRect()
|
||||
const margin = 20 // account for a scroll bar
|
||||
|
@ -318,7 +326,8 @@ export default class JSONNode extends Component {
|
|||
|
||||
renderExpandButton () {
|
||||
const className = `jsoneditor-button jsoneditor-${this.props.data.expanded ? 'expanded' : 'collapsed'}`
|
||||
return h('div', {className: 'jsoneditor-button-container'},
|
||||
|
||||
return h('div', {key: 'expand', className: 'jsoneditor-button-container'},
|
||||
h('button', {
|
||||
className: className,
|
||||
onClick: this.handleExpand,
|
||||
|
@ -331,6 +340,7 @@ export default class JSONNode extends Component {
|
|||
|
||||
renderActionMenuButton () {
|
||||
return h(ActionButton, {
|
||||
key: 'action',
|
||||
path: this.getPath(),
|
||||
type: this.props.data.type,
|
||||
events: this.props.events
|
||||
|
@ -339,6 +349,7 @@ export default class JSONNode extends Component {
|
|||
|
||||
renderAppendMenuButton () {
|
||||
return h(AppendActionButton, {
|
||||
key: 'append',
|
||||
path: this.getPath(),
|
||||
events: this.props.events
|
||||
})
|
||||
|
|
|
@ -26,6 +26,7 @@ export default class JSONNodeForm extends JSONNode {
|
|||
|
||||
if (isIndex) { // array item
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property jsoneditor-readonly'
|
||||
}, prop)
|
||||
}
|
||||
|
@ -33,6 +34,7 @@ export default class JSONNodeForm extends JSONNode {
|
|||
const escapedProp = escapeHTML(prop, options.escapeUnicode)
|
||||
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property' + (prop.length === 0 ? ' jsoneditor-empty' : '')
|
||||
}, escapedProp)
|
||||
}
|
||||
|
@ -42,6 +44,7 @@ export default class JSONNodeForm extends JSONNode {
|
|||
const content = JSONNode.getRootName(data, options)
|
||||
|
||||
return h('div', {
|
||||
key: 'property',
|
||||
className: 'jsoneditor-property jsoneditor-readonly'
|
||||
}, content)
|
||||
}
|
||||
|
|
|
@ -22,12 +22,14 @@ export default class JSONNodeView extends JSONNodeForm {
|
|||
|
||||
if (itsAnUrl) {
|
||||
return h('a', {
|
||||
key: 'value',
|
||||
className: className,
|
||||
href: escapedValue
|
||||
}, escapedValue)
|
||||
}
|
||||
else {
|
||||
return h('div', {
|
||||
key: 'value',
|
||||
className: className,
|
||||
onClick: this.handleClickValue
|
||||
}, escapedValue)
|
||||
|
|
|
@ -48,18 +48,17 @@ export default class TextMode extends Component {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { props, state} = this
|
||||
|
||||
return h('div', {className: 'jsoneditor jsoneditor-mode-text'}, [
|
||||
this.renderMenu(),
|
||||
|
||||
h('div', {className: 'jsoneditor-contents'}, [
|
||||
h('div', {key: 'contents', className: 'jsoneditor-contents'},
|
||||
h('textarea', {
|
||||
className: 'jsoneditor-text',
|
||||
value: this.state.text,
|
||||
onInput: this.handleChange
|
||||
onChange: this.handleChange,
|
||||
onInput: this.handleInput
|
||||
})
|
||||
]),
|
||||
),
|
||||
|
||||
this.renderSchemaErrors ()
|
||||
])
|
||||
|
@ -68,13 +67,15 @@ export default class TextMode extends Component {
|
|||
/** @protected */
|
||||
renderMenu () {
|
||||
// TODO: move Menu into a separate Component
|
||||
return h('div', {className: 'jsoneditor-menu'}, [
|
||||
return h('div', {key: 'menu', className: 'jsoneditor-menu'}, [
|
||||
h('button', {
|
||||
key: 'format',
|
||||
className: 'jsoneditor-format',
|
||||
title: 'Format the JSON document',
|
||||
onClick: this.handleFormat
|
||||
}),
|
||||
h('button', {
|
||||
key: 'compact',
|
||||
className: 'jsoneditor-compact',
|
||||
title: 'Compact the JSON document',
|
||||
onClick: this.handleCompact
|
||||
|
@ -82,9 +83,13 @@ export default class TextMode extends Component {
|
|||
|
||||
// TODO: implement a button "Repair"
|
||||
|
||||
h('div', {className: 'jsoneditor-vertical-menu-separator'}),
|
||||
h('div', {
|
||||
key: 'separator',
|
||||
className: 'jsoneditor-vertical-menu-separator'
|
||||
}),
|
||||
|
||||
this.props.options.modes && h(ModeButton, {
|
||||
key: 'mode',
|
||||
modes: this.props.options.modes,
|
||||
mode: this.props.mode,
|
||||
onChangeMode: this.props.onChangeMode,
|
||||
|
@ -110,7 +115,7 @@ export default class TextMode extends Component {
|
|||
|
||||
console.log('errors', allErrors)
|
||||
|
||||
return h('div', { class: 'jsoneditor-errors'},
|
||||
return h('div', { className: 'jsoneditor-errors'},
|
||||
h('table', {},
|
||||
h('tbody', {}, limitedErrors.map(TextMode.renderSchemaError))
|
||||
)
|
||||
|
@ -124,7 +129,7 @@ export default class TextMode extends Component {
|
|||
// no valid JSON
|
||||
// TODO: display errors in text mode somehow? shouldn't be too much in your face
|
||||
// maybe a warning icon top right?
|
||||
// return h('table', {class: 'jsoneditor-text-errors'},
|
||||
// return h('table', {className: 'jsoneditor-text-errors'},
|
||||
// h('tbody', {}, TextMode.renderSchemaError(err))
|
||||
// )
|
||||
return null
|
||||
|
@ -137,22 +142,22 @@ export default class TextMode extends Component {
|
|||
* @return {JSX.Element}
|
||||
*/
|
||||
static renderSchemaError (error) {
|
||||
const icon = h('input', {type: 'button', class: 'jsoneditor-schema-error'})
|
||||
const icon = h('input', {type: 'button', className: 'jsoneditor-schema-error'})
|
||||
|
||||
if (error && error.schema && error.schemaPath) {
|
||||
// this is an ajv error message
|
||||
return h('tr', {}, [
|
||||
h('td', {}, icon),
|
||||
h('td', {}, error.dataPath),
|
||||
h('td', {}, error.message)
|
||||
h('td', {key: 'icon'}, icon),
|
||||
h('td', {key: 'path'}, error.dataPath),
|
||||
h('td', {key: 'message'}, error.message)
|
||||
])
|
||||
}
|
||||
else {
|
||||
// any other error message
|
||||
console.log('error???', error)
|
||||
return h('tr', {},
|
||||
h('td', {}, icon),
|
||||
h('td', {colSpan: 2}, h('code', {}, String(error)))
|
||||
h('td', {key: 'icon'}, icon),
|
||||
h('td', {key: 'message', colSpan: 2}, h('code', {}, String(error)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -191,12 +196,16 @@ export default class TextMode extends Component {
|
|||
return this.props.options && this.props.options.indentation || 2
|
||||
}
|
||||
|
||||
handleChange = (event) => {
|
||||
// do nothing...
|
||||
}
|
||||
|
||||
/**
|
||||
* handle changed text input in the textarea
|
||||
* @param {Event} event
|
||||
* @protected
|
||||
*/
|
||||
handleChange = (event) => {
|
||||
handleInput = (event) => {
|
||||
this.setText(event.target.value)
|
||||
|
||||
if (this.props.options && this.props.options.onChangeText) {
|
||||
|
@ -229,8 +238,8 @@ export default class TextMode extends Component {
|
|||
* Format the json
|
||||
*/
|
||||
format () {
|
||||
var json = this.get()
|
||||
var text = JSON.stringify(json, null, this.getIndentation())
|
||||
const json = this.get()
|
||||
const text = JSON.stringify(json, null, this.getIndentation())
|
||||
this.setText(text)
|
||||
}
|
||||
|
||||
|
@ -238,8 +247,8 @@ export default class TextMode extends Component {
|
|||
* Compact the json
|
||||
*/
|
||||
compact () {
|
||||
var json = this.get()
|
||||
var text = JSON.stringify(json)
|
||||
const json = this.get()
|
||||
const text = JSON.stringify(json)
|
||||
this.setText(text)
|
||||
}
|
||||
|
||||
|
|
|
@ -81,9 +81,7 @@ export default class TreeMode extends Component {
|
|||
data = addSearchResults(data, searchResults)
|
||||
|
||||
data = addFocus(data, searchResults[0]) // TODO: change to using focus from state
|
||||
|
||||
}
|
||||
console.log('render', data)
|
||||
// TODO: pass number of search results to search box in top menu
|
||||
|
||||
return h('div', {
|
||||
|
@ -92,7 +90,7 @@ export default class TreeMode extends Component {
|
|||
}, [
|
||||
this.renderMenu(),
|
||||
|
||||
h('div', {className: 'jsoneditor-contents jsoneditor-tree-contents', onClick: this.handleHideMenus},
|
||||
h('div', {key: 'contents', className: 'jsoneditor-contents jsoneditor-tree-contents', onClick: this.handleHideMenus},
|
||||
h('ul', {className: 'jsoneditor-list jsoneditor-root'},
|
||||
h(Node, {
|
||||
data,
|
||||
|
@ -109,11 +107,13 @@ export default class TreeMode extends Component {
|
|||
renderMenu () {
|
||||
let items = [
|
||||
h('button', {
|
||||
key: 'expand-all',
|
||||
className: 'jsoneditor-expand-all',
|
||||
title: 'Expand all objects and arrays',
|
||||
onClick: this.handleExpandAll
|
||||
}),
|
||||
h('button', {
|
||||
key: 'collapse-all',
|
||||
className: 'jsoneditor-collapse-all',
|
||||
title: 'Collapse all objects and arrays',
|
||||
onClick: this.handleCollapseAll
|
||||
|
@ -122,17 +122,17 @@ export default class TreeMode extends Component {
|
|||
|
||||
if (this.props.mode !== 'view' && this.props.options.history != false) {
|
||||
items = items.concat([
|
||||
h('div', {className: 'jsoneditor-vertical-menu-separator'}),
|
||||
h('div', {key: 'history-separator', className: 'jsoneditor-vertical-menu-separator'}),
|
||||
|
||||
h('div', {style: {display: 'inline-block'}}, [
|
||||
h('button', {
|
||||
className: 'jsoneditor-undo',
|
||||
title: 'Undo last action',
|
||||
disabled: !this.canUndo(),
|
||||
onClick: this.undo
|
||||
}),
|
||||
]),
|
||||
h('button', {
|
||||
key: 'undo',
|
||||
className: 'jsoneditor-undo',
|
||||
title: 'Undo last action',
|
||||
disabled: !this.canUndo(),
|
||||
onClick: this.undo
|
||||
}),
|
||||
h('button', {
|
||||
key: 'redo',
|
||||
className: 'jsoneditor-redo',
|
||||
title: 'Redo',
|
||||
disabled: !this.canRedo(),
|
||||
|
@ -143,9 +143,10 @@ export default class TreeMode extends Component {
|
|||
|
||||
if (this.props.options.modes ) {
|
||||
items = items.concat([
|
||||
h('div', {className: 'jsoneditor-vertical-menu-separator'}),
|
||||
h('div', {key: 'mode-separator', className: 'jsoneditor-vertical-menu-separator'}),
|
||||
|
||||
h(ModeButton, {
|
||||
key: 'mode',
|
||||
modes: this.props.options.modes,
|
||||
mode: this.props.mode,
|
||||
onChangeMode: this.props.onChangeMode,
|
||||
|
@ -157,7 +158,7 @@ export default class TreeMode extends Component {
|
|||
if (this.props.options.search !== false) {
|
||||
// option search is true or undefined
|
||||
items = items.concat([
|
||||
h('div', {class: 'jsoneditor-menu-panel-right'},
|
||||
h('div', {key: 'search', className: 'jsoneditor-menu-panel-right'},
|
||||
h(Search, {
|
||||
text: this.state.search.text,
|
||||
onChange: this.handleSearch,
|
||||
|
@ -167,7 +168,7 @@ export default class TreeMode extends Component {
|
|||
])
|
||||
}
|
||||
|
||||
return h('div', {className: 'jsoneditor-menu'}, items)
|
||||
return h('div', {key: 'menu', className: 'jsoneditor-menu'}, items)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,11 +26,16 @@ export default class ActionButton extends Component {
|
|||
|
||||
return h('div', {className: 'jsoneditor-button-container'}, [
|
||||
h(ActionMenu, {
|
||||
key: 'menu',
|
||||
...props, // path, type, events
|
||||
...state, // open, anchor, root
|
||||
onRequestClose: this.handleRequestClose
|
||||
}),
|
||||
h('button', {className: className, onClick: this.handleOpen})
|
||||
h('button', {
|
||||
key: 'button',
|
||||
className,
|
||||
onClick: this.handleOpen
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,16 @@ export default class AppendActionButton extends Component {
|
|||
|
||||
return h('div', {className: 'jsoneditor-button-container'}, [
|
||||
h(AppendActionMenu, {
|
||||
key: 'menu',
|
||||
...props, // path, events
|
||||
...state, // open, anchor, root
|
||||
onRequestClose: this.handleRequestClose
|
||||
}),
|
||||
h('button', {className: className, onClick: this.handleOpen})
|
||||
h('button', {
|
||||
key: 'button',
|
||||
className: className,
|
||||
onClick: this.handleOpen
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export default class AppendActionMenu extends Component {
|
|||
const { props, state} = this
|
||||
|
||||
const items = [
|
||||
createAppend(props.path, props.events.onAppend)
|
||||
createAppend(props.path, props.events.onAppend)
|
||||
]
|
||||
|
||||
// TODO: implement a hook to adjust the action menu
|
||||
|
|
|
@ -20,9 +20,7 @@ export default class Menu extends Component {
|
|||
* @return {*}
|
||||
*/
|
||||
render () {
|
||||
const { props, state} = this
|
||||
|
||||
if (!props.open) {
|
||||
if (!this.props.open) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -44,13 +42,13 @@ export default class Menu extends Component {
|
|||
className: className,
|
||||
'data-menu': 'true'
|
||||
},
|
||||
props.items.map(this.renderMenuItem)
|
||||
this.props.items.map(this.renderMenuItem)
|
||||
)
|
||||
}
|
||||
|
||||
renderMenuItem = (item, index) => {
|
||||
if (item.type === 'separator') {
|
||||
return h('div', {className: 'jsoneditor-menu-separator'})
|
||||
return h('div', {key: index, className: 'jsoneditor-menu-separator'})
|
||||
}
|
||||
|
||||
if (item.click && item.submenu) {
|
||||
|
@ -61,24 +59,24 @@ export default class Menu extends Component {
|
|||
}
|
||||
|
||||
// two buttons: direct click and a small button to expand the submenu
|
||||
return h('div', {className: 'jsoneditor-menu-item'}, [
|
||||
h('button', {className: 'jsoneditor-menu-button jsoneditor-menu-default ' + item.className, title: item.title, onClick }, [
|
||||
h('span', {className: 'jsoneditor-icon'}),
|
||||
h('span', {className: 'jsoneditor-text'}, item.text)
|
||||
return h('div', {key: index, className: 'jsoneditor-menu-item'}, [
|
||||
h('button', {key: 'default', className: 'jsoneditor-menu-button jsoneditor-menu-default ' + item.className, title: item.title, onClick }, [
|
||||
h('span', {key: 'icon', className: 'jsoneditor-icon'}),
|
||||
h('span', {key: 'text', className: 'jsoneditor-text'}, item.text)
|
||||
]),
|
||||
h('button', {className: 'jsoneditor-menu-button jsoneditor-menu-expand', onClick: this.createExpandHandler(index) }, [
|
||||
h('button', {key: 'expand', className: 'jsoneditor-menu-button jsoneditor-menu-expand', onClick: this.createExpandHandler(index) },
|
||||
h('span', {className: 'jsoneditor-icon jsoneditor-icon-expand'})
|
||||
]),
|
||||
),
|
||||
this.renderSubMenu(item.submenu, index)
|
||||
])
|
||||
}
|
||||
else if (item.submenu) {
|
||||
// button expands the submenu
|
||||
return h('div', {className: 'jsoneditor-menu-item'}, [
|
||||
h('button', {className: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: this.createExpandHandler(index) }, [
|
||||
h('span', {className: 'jsoneditor-icon'}),
|
||||
h('span', {className: 'jsoneditor-text'}, item.text),
|
||||
h('span', {className: 'jsoneditor-icon jsoneditor-icon-expand'}),
|
||||
return h('div', {key: index, className: 'jsoneditor-menu-item'}, [
|
||||
h('button', {key: 'default', className: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: this.createExpandHandler(index) }, [
|
||||
h('span', {key: 'icon', className: 'jsoneditor-icon'}),
|
||||
h('span', {key: 'text', className: 'jsoneditor-text'}, item.text),
|
||||
h('span', {key: 'expand', className: 'jsoneditor-icon jsoneditor-icon-expand'}),
|
||||
]),
|
||||
this.renderSubMenu(item.submenu, index)
|
||||
])
|
||||
|
@ -91,12 +89,12 @@ export default class Menu extends Component {
|
|||
}
|
||||
|
||||
// just a button (no submenu)
|
||||
return h('div', {className: 'jsoneditor-menu-item'}, [
|
||||
return h('div', {key: index, className: 'jsoneditor-menu-item'},
|
||||
h('button', {className: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick }, [
|
||||
h('span', {className: 'jsoneditor-icon'}),
|
||||
h('span', {className: 'jsoneditor-text'}, item.text)
|
||||
h('span', {key: 'icon', className: 'jsoneditor-icon'}),
|
||||
h('span', {key: 'text', className: 'jsoneditor-text'}, item.text)
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,26 +106,26 @@ export default class Menu extends Component {
|
|||
const expanded = this.state.expanded === index
|
||||
const collapsing = this.state.collapsing === index
|
||||
|
||||
const contents = submenu.map(item => {
|
||||
const contents = submenu.map((item, index) => {
|
||||
// FIXME: don't create functions in the render function
|
||||
const onClick = () => {
|
||||
item.click()
|
||||
this.props.onRequestClose()
|
||||
}
|
||||
|
||||
return h('div', {className: 'jsoneditor-menu-item'}, [
|
||||
return h('div', {key: index, className: 'jsoneditor-menu-item'},
|
||||
h('button', {className: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick }, [
|
||||
h('span', {className: 'jsoneditor-icon'}),
|
||||
h('span', {className: 'jsoneditor-text'}, item.text)
|
||||
h('span', {key: 'icon', className: 'jsoneditor-icon'}),
|
||||
h('span', {key: 'text', className: 'jsoneditor-text'}, item.text)
|
||||
]),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
const className = 'jsoneditor-submenu ' +
|
||||
(expanded ? ' jsoneditor-expanded' : '') +
|
||||
(collapsing ? ' jsoneditor-collapsing' : '')
|
||||
|
||||
return h('div', {className: className}, contents)
|
||||
return h('div', {key: 'submenu', className: className}, contents)
|
||||
}
|
||||
|
||||
createExpandHandler (index) {
|
||||
|
|
|
@ -21,11 +21,13 @@ export default class ModeButton extends Component {
|
|||
|
||||
return h('div', {className: 'jsoneditor-modes'}, [
|
||||
h('button', {
|
||||
key: 'button',
|
||||
title: 'Switch mode',
|
||||
onClick: this.handleOpen
|
||||
}, `${toCapital(props.mode)} \u25BC`),
|
||||
|
||||
h(ModeMenu, {
|
||||
key: 'menu',
|
||||
...props,
|
||||
open: state.open,
|
||||
onRequestClose: this.handleRequestClose
|
||||
|
|
|
@ -14,13 +14,14 @@ export default class ModeMenu extends Component {
|
|||
if (props.open) {
|
||||
const items = props.modes.map(mode => {
|
||||
return h('button', {
|
||||
key: mode,
|
||||
title: `Switch to ${mode} mode`,
|
||||
className: 'jsoneditor-menu-button jsoneditor-type-modes' +
|
||||
((mode === props.mode) ? ' jsoneditor-selected' : ''),
|
||||
onClick: () => {
|
||||
try {
|
||||
props.onChangeMode(mode)
|
||||
props.onRequestClose()
|
||||
props.onChangeMode(mode)
|
||||
}
|
||||
catch (err) {
|
||||
props.onError(err)
|
||||
|
@ -30,8 +31,7 @@ export default class ModeMenu extends Component {
|
|||
})
|
||||
|
||||
return h('div', {
|
||||
className: 'jsoneditor-actionmenu jsoneditor-modemenu',
|
||||
nodemenu: 'true',
|
||||
className: 'jsoneditor-actionmenu jsoneditor-modemenu'
|
||||
}, items)
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { h, Component } from 'preact'
|
||||
import { createElement as h, Component } from 'react'
|
||||
|
||||
import '!style!css!less!./Search.less'
|
||||
|
||||
|
@ -11,14 +11,14 @@ export default class Search extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
render (props, state) {
|
||||
render () {
|
||||
// TODO: show number of search results left from the input box
|
||||
// TODO: prev/next
|
||||
// TODO: focus on search results
|
||||
// TODO: expand the focused search result if not expanded
|
||||
|
||||
return h('div', {class: 'jsoneditor-search'},
|
||||
h('input', {type: 'text', value: state.text, onInput: this.handleChange})
|
||||
return h('div', {className: 'jsoneditor-search'},
|
||||
h('input', {type: 'text', value: this.state.text, onInput: this.handleChange})
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,18 @@
|
|||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// prevent contentEditable warnings of React
|
||||
// https://github.com/facebook/draft-js/issues/53#issuecomment-188280259
|
||||
console.error = (function() {
|
||||
const error = console.error
|
||||
|
||||
return function(exception) {
|
||||
if ((exception + '').indexOf('Warning: A component is `contentEditable`') != 0) {
|
||||
error.apply(console, arguments)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
// create the editor
|
||||
const mode = document.getElementById('mode').value
|
||||
const container = document.getElementById('container')
|
||||
|
@ -136,7 +148,6 @@
|
|||
editor.setMode(mode)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
26
src/index.js
26
src/index.js
|
@ -1,5 +1,5 @@
|
|||
import { createElement as h, Component } from 'react'
|
||||
import { render } from 'react-dom'
|
||||
import { render, unmountComponentAtNode} from 'react-dom'
|
||||
import CodeMode from './components/CodeMode'
|
||||
import TextMode from './components/TextMode'
|
||||
import TreeMode from './components/TreeMode'
|
||||
|
@ -36,7 +36,6 @@ function jsoneditor (container, options = {}) {
|
|||
_schema: null,
|
||||
_modes: modes,
|
||||
_mode: null,
|
||||
_element: null,
|
||||
_component: null
|
||||
}
|
||||
|
||||
|
@ -142,8 +141,7 @@ function jsoneditor (container, options = {}) {
|
|||
|
||||
let success = false
|
||||
let initialChildCount = editor._container.children.length
|
||||
let element
|
||||
let component
|
||||
let component = null
|
||||
try {
|
||||
// find the constructor for the selected mode
|
||||
const constructor = editor._modes[mode]
|
||||
|
@ -183,7 +181,7 @@ function jsoneditor (container, options = {}) {
|
|||
|
||||
// apply JSON schema (if any)
|
||||
try {
|
||||
element._component.setSchema(editor._schema)
|
||||
component.setSchema(editor._schema)
|
||||
}
|
||||
catch (err) {
|
||||
handleError(err)
|
||||
|
@ -192,7 +190,6 @@ function jsoneditor (container, options = {}) {
|
|||
// set JSON (this can throw an error)
|
||||
const text = editor._component ? editor._component.getText() : '{}'
|
||||
component.setText(text)
|
||||
element = editor._container.lastChild
|
||||
|
||||
// when setText didn't fail, we will reach this point
|
||||
success = true
|
||||
|
@ -202,13 +199,7 @@ function jsoneditor (container, options = {}) {
|
|||
}
|
||||
finally {
|
||||
if (success) {
|
||||
// destroy previous component
|
||||
if (editor._element) {
|
||||
unrender(container, editor._element)
|
||||
}
|
||||
|
||||
editor._mode = mode
|
||||
editor._element = element
|
||||
editor._component = component
|
||||
}
|
||||
else {
|
||||
|
@ -228,7 +219,7 @@ function jsoneditor (container, options = {}) {
|
|||
* Remove the editor from the DOM and clean up workers
|
||||
*/
|
||||
editor.destroy = function () {
|
||||
unrender(container, editor._element)
|
||||
unmountComponentAtNode(editor._container)
|
||||
}
|
||||
|
||||
editor.setMode(options && options.mode || 'tree')
|
||||
|
@ -242,13 +233,4 @@ jsoneditor.utils = {
|
|||
parseJSONPointer
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a rendered preact component
|
||||
* @param container
|
||||
* @param root
|
||||
*/
|
||||
function unrender (container, root) {
|
||||
render('', container, root);
|
||||
}
|
||||
|
||||
module.exports = jsoneditor
|
||||
|
|
Loading…
Reference in New Issue