Try react

This commit is contained in:
jos 2016-10-28 22:21:44 +02:00
parent 3f3fef9b40
commit a7c77ff9d1
15 changed files with 130 additions and 102 deletions

View File

@ -27,7 +27,9 @@
"brace": "0.9.0",
"javascript-natural-sort": "0.7.1",
"lodash": "4.16.4",
"preact": "6.4.0"
"preact": "6.4.0",
"react": "^15.3.2",
"react-dom": "^15.3.2"
},
"devDependencies": {
"ava": "0.16.0",

View File

@ -1,4 +1,4 @@
import { h } from 'preact'
import { createElement as h, Component } from 'react'
import TextMode from './TextMode'
import ace from '../assets/ace'
@ -36,11 +36,12 @@ export default class CodeMode extends TextMode {
this.aceEditor = null
}
render (props, state) {
return h('div', {class: 'jsoneditor jsoneditor-mode-code'}, [
render () {
const { props, state } = this
return h('div', {className: 'jsoneditor jsoneditor-mode-code'}, [
this.renderMenu(),
h('div', {class: 'jsoneditor-contents', id: this.id})
h('div', {className: 'jsoneditor-contents', id: this.id})
])
}

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import ActionButton from './menu/ActionButton'
import AppendActionButton from './menu/AppendActionButton'
@ -24,7 +24,9 @@ export default class JSONNode extends Component {
}
}
render (props, state) {
render () {
const { props } = this
if (props.data.type === 'Array') {
return this.renderJSONArray(props)
}
@ -39,7 +41,7 @@ export default class JSONNode extends Component {
renderJSONObject ({prop, data, options, events}) {
const childCount = data.props.length
const contents = [
h('div', {class: 'jsoneditor-node jsoneditor-object'}, [
h('div', {className: 'jsoneditor-node jsoneditor-object'}, [
this.renderExpandButton(),
this.renderActionMenuButton(),
this.renderProperty(prop, data, options),
@ -60,10 +62,10 @@ export default class JSONNode extends Component {
})
})
contents.push(h('ul', {key: 'props', class: 'jsoneditor-list'}, props))
contents.push(h('ul', {key: 'props', className: 'jsoneditor-list'}, props))
}
else {
contents.push(h('ul', {key: 'append', class: 'jsoneditor-list'}, [
contents.push(h('ul', {key: 'append', className: 'jsoneditor-list'}, [
this.renderAppend('(empty object)')
]))
}
@ -75,7 +77,7 @@ export default class JSONNode extends Component {
renderJSONArray ({prop, data, options, events}) {
const childCount = data.items.length
const contents = [
h('div', {class: 'jsoneditor-node jsoneditor-array'}, [
h('div', {className: 'jsoneditor-node jsoneditor-array'}, [
this.renderExpandButton(),
this.renderActionMenuButton(),
this.renderProperty(prop, data, options),
@ -95,10 +97,10 @@ export default class JSONNode extends Component {
events
})
})
contents.push(h('ul', {key: 'items', class: 'jsoneditor-list'}, items))
contents.push(h('ul', {key: 'items', className: 'jsoneditor-list'}, items))
}
else {
contents.push(h('ul', {key: 'append', class: 'jsoneditor-list'}, [
contents.push(h('ul', {key: 'append', className: 'jsoneditor-list'}, [
this.renderAppend('(empty array)')
]))
}
@ -109,7 +111,7 @@ export default class JSONNode extends Component {
renderJSONValue ({prop, data, options}) {
return h('li', {}, [
h('div', {class: 'jsoneditor-node'}, [
h('div', {className: 'jsoneditor-node'}, [
this.renderPlaceholder(),
this.renderActionMenuButton(),
this.renderProperty(prop, data, options),
@ -126,7 +128,7 @@ export default class JSONNode extends Component {
*/
renderAppend (text) {
return h('li', {key: 'append'}, [
h('div', {class: 'jsoneditor-node'}, [
h('div', {className: 'jsoneditor-node'}, [
this.renderPlaceholder(),
this.renderAppendMenuButton(),
this.renderReadonly(text)
@ -135,11 +137,11 @@ export default class JSONNode extends Component {
}
renderPlaceholder () {
return h('div', {class: 'jsoneditor-button-placeholder'})
return h('div', {className: 'jsoneditor-button-placeholder'})
}
renderReadonly (text, title = null) {
return h('div', {class: 'jsoneditor-readonly', title}, text)
return h('div', {className: 'jsoneditor-readonly', title}, text)
}
renderProperty (prop, data, options) {
@ -148,7 +150,7 @@ export default class JSONNode extends Component {
const rootName = JSONNode.getRootName(data, options)
return h('div', {
class: 'jsoneditor-property jsoneditor-readonly',
className: 'jsoneditor-property jsoneditor-readonly',
spellCheck: 'false',
onBlur: this.handleChangeProperty
}, rootName)
@ -161,7 +163,7 @@ export default class JSONNode extends Component {
const escapedProp = escapeHTML(prop, options.escapeUnicode)
return h('div', {
class: 'jsoneditor-property' + (prop.length === 0 ? ' jsoneditor-empty' : ''),
className: 'jsoneditor-property' + (prop.length === 0 ? ' jsoneditor-empty' : ''),
contentEditable: 'true',
spellCheck: 'false',
onBlur: this.handleChangeProperty
@ -169,14 +171,14 @@ export default class JSONNode extends Component {
}
else {
return h('div', {
class: 'jsoneditor-property jsoneditor-readonly',
className: 'jsoneditor-property jsoneditor-readonly',
spellCheck: 'false'
}, prop)
}
}
renderSeparator() {
return h('div', {class: 'jsoneditor-separator'}, ':')
return h('div', {className: 'jsoneditor-separator'}, ':')
}
renderValue (value, options) {
@ -188,7 +190,7 @@ export default class JSONNode extends Component {
const editable = !options.isValueEditable || options.isValueEditable(this.getPath())
if (editable) {
return h('div', {
class: JSONNode.getValueClass(type, itsAnUrl, isEmpty),
className: JSONNode.getValueClass(type, itsAnUrl, isEmpty),
contentEditable: 'true',
spellCheck: 'false',
onBlur: this.handleChangeValue,
@ -200,7 +202,7 @@ export default class JSONNode extends Component {
}
else {
return h('div', {
class: 'jsoneditor-readonly',
className: 'jsoneditor-readonly',
title: itsAnUrl ? JSONNode.URL_TITLE : null
}, escapedValue)
}
@ -262,9 +264,9 @@ export default class JSONNode extends Component {
renderExpandButton () {
const className = `jsoneditor-button jsoneditor-${this.props.data.expanded ? 'expanded' : 'collapsed'}`
return h('div', {class: 'jsoneditor-button-container'},
return h('div', {className: 'jsoneditor-button-container'},
h('button', {
class: className,
className: className,
onClick: this.handleExpand,
title:
'Click to expand/collapse this field. \n' +

View File

@ -1,5 +1,4 @@
import { h } from 'preact'
import { createElement as h } from 'react'
import { escapeHTML } from '../utils/stringUtils'
import JSONNode from './JSONNode'
@ -27,14 +26,14 @@ export default class JSONNodeForm extends JSONNode {
if (isIndex) { // array item
return h('div', {
class: 'jsoneditor-property jsoneditor-readonly'
className: 'jsoneditor-property jsoneditor-readonly'
}, prop)
}
else { // object property
const escapedProp = escapeHTML(prop, options.escapeUnicode)
return h('div', {
class: 'jsoneditor-property' + (prop.length === 0 ? ' jsoneditor-empty' : '')
className: 'jsoneditor-property' + (prop.length === 0 ? ' jsoneditor-empty' : '')
}, escapedProp)
}
}
@ -43,7 +42,7 @@ export default class JSONNodeForm extends JSONNode {
const content = JSONNode.getRootName(data, options)
return h('div', {
class: 'jsoneditor-property jsoneditor-readonly'
className: 'jsoneditor-property jsoneditor-readonly'
}, content)
}
}

View File

@ -1,4 +1,4 @@
import { h } from 'preact'
import { createElement as h } from 'react'
import { escapeHTML } from '../utils/stringUtils'
import { valueType, isUrl } from '../utils/typeUtils'
@ -22,13 +22,13 @@ export default class JSONNodeView extends JSONNodeForm {
if (itsAnUrl) {
return h('a', {
class: className,
className: className,
href: escapedValue
}, escapedValue)
}
else {
return h('div', {
class: className,
className: className,
onClick: this.handleClickValue
}, escapedValue)
}

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import { parseJSON } from '../utils/jsonUtils'
import { escapeUnicodeChars } from '../utils/stringUtils'
import { jsonToData, dataToJson, patchData } from '../jsonData'
@ -37,13 +37,15 @@ export default class TextMode extends Component {
}
}
render (props, state) {
return h('div', {class: 'jsoneditor jsoneditor-mode-text'}, [
render () {
const { props, state} = this
return h('div', {className: 'jsoneditor jsoneditor-mode-text'}, [
this.renderMenu(),
h('div', {class: 'jsoneditor-contents'}, [
h('div', {className: 'jsoneditor-contents'}, [
h('textarea', {
class: 'jsoneditor-text',
className: 'jsoneditor-text',
value: this.state.text,
onInput: this.handleChange
})
@ -53,21 +55,21 @@ export default class TextMode extends Component {
/** @protected */
renderMenu () {
return h('div', {class: 'jsoneditor-menu'}, [
return h('div', {className: 'jsoneditor-menu'}, [
h('button', {
class: 'jsoneditor-format',
className: 'jsoneditor-format',
title: 'Format the JSON document',
onClick: this.handleFormat
}),
h('button', {
class: 'jsoneditor-compact',
className: 'jsoneditor-compact',
title: 'Compact the JSON document',
onClick: this.handleCompact
}),
// TODO: implement a button "Repair"
h('div', {class: 'jsoneditor-vertical-menu-separator'}),
h('div', {className: 'jsoneditor-vertical-menu-separator'}),
this.props.options.modes && h(ModeButton, {
modes: this.props.options.modes,

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import { updateIn } from '../utils/immutabilityHelpers'
import { expand, jsonToData, dataToJson, toDataPath, patchData } from '../jsonData'
@ -43,7 +43,9 @@ export default class TreeMode extends Component {
}
}
render (props, state) {
render () {
const { props, state } = this
const Node = (props.mode === 'view')
? JSONNodeView
: (props.mode === 'form')
@ -51,13 +53,13 @@ export default class TreeMode extends Component {
: JSONNode
return h('div', {
class: `jsoneditor jsoneditor-mode-${props.mode}`,
className: `jsoneditor jsoneditor-mode-${props.mode}`,
'data-jsoneditor': 'true'
}, [
this.renderMenu(),
h('div', {class: 'jsoneditor-contents jsoneditor-tree-contents', onClick: this.handleHideMenus}, [
h('ul', {class: 'jsoneditor-list jsoneditor-root'}, [
h('div', {className: 'jsoneditor-contents jsoneditor-tree-contents', onClick: this.handleHideMenus}, [
h('ul', {className: 'jsoneditor-list jsoneditor-root'}, [
h(Node, {
data: state.data,
events: state.events,
@ -73,12 +75,12 @@ export default class TreeMode extends Component {
renderMenu () {
let items = [
h('button', {
class: 'jsoneditor-expand-all',
className: 'jsoneditor-expand-all',
title: 'Expand all objects and arrays',
onClick: this.handleExpandAll
}),
h('button', {
class: 'jsoneditor-collapse-all',
className: 'jsoneditor-collapse-all',
title: 'Collapse all objects and arrays',
onClick: this.handleCollapseAll
})
@ -86,18 +88,18 @@ export default class TreeMode extends Component {
if (this.props.mode !== 'view' && this.props.options.history != false) {
items = items.concat([
h('div', {class: 'jsoneditor-vertical-menu-separator'}),
h('div', {className: 'jsoneditor-vertical-menu-separator'}),
h('div', {style: 'display:inline-block'}, [
h('div', {style: {display: 'inline-block'}}, [
h('button', {
class: 'jsoneditor-undo',
className: 'jsoneditor-undo',
title: 'Undo last action',
disabled: !this.canUndo(),
onClick: this.undo
}),
]),
h('button', {
class: 'jsoneditor-redo',
className: 'jsoneditor-redo',
title: 'Redo',
disabled: !this.canRedo(),
onClick: this.redo
@ -107,7 +109,7 @@ export default class TreeMode extends Component {
if (this.props.options.modes ) {
items = items.concat([
h('div', {class: 'jsoneditor-vertical-menu-separator'}),
h('div', {className: 'jsoneditor-vertical-menu-separator'}),
h(ModeButton, {
modes: this.props.options.modes,
@ -118,7 +120,7 @@ export default class TreeMode extends Component {
])
}
return h('div', {class: 'jsoneditor-menu'}, items)
return h('div', {className: 'jsoneditor-menu'}, items)
}
/** @private */

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import ActionMenu from './ActionMenu'
import { findParentNode } from '../../utils/domUtils'
@ -18,17 +18,19 @@ export default class ActionButton extends Component {
* @param state
* @return {*}
*/
render (props, state) {
render () {
const { props, state} = this
const className = 'jsoneditor-button jsoneditor-actionmenu' +
(this.state.open ? ' jsoneditor-visible' : '')
return h('div', {class: 'jsoneditor-button-container'}, [
return h('div', {className: 'jsoneditor-button-container'}, [
h(ActionMenu, {
...props, // path, type, events
...state, // open, anchor, root
onRequestClose: this.handleRequestClose
}),
h('button', {class: className, onClick: this.handleOpen})
h('button', {className: className, onClick: this.handleOpen})
])
}

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import Menu from './Menu'
import {
createChangeType, createSort,
@ -12,7 +12,9 @@ export default class ActionMenu extends Component {
* @param state
* @return {JSX.Element}
*/
render (props, state) {
render () {
const { props, state} = this
let items = [] // array with menu items
items.push(createChangeType(props.path, props.type, props.events.onChangeType))

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import AppendActionMenu from './AppendActionMenu'
import { findParentNode } from '../../utils/domUtils'
@ -18,17 +18,19 @@ export default class AppendActionButton extends Component {
* @param state
* @return {*}
*/
render (props, state) {
render () {
const { props, state} = this
const className = 'jsoneditor-button jsoneditor-actionmenu' +
(this.state.open ? ' jsoneditor-visible' : '')
return h('div', {class: 'jsoneditor-button-container'}, [
return h('div', {className: 'jsoneditor-button-container'}, [
h(AppendActionMenu, {
...props, // path, events
...state, // open, anchor, root
onRequestClose: this.handleRequestClose
}),
h('button', {class: className, onClick: this.handleOpen})
h('button', {className: className, onClick: this.handleOpen})
])
}

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import Menu from './Menu'
import { createAppend } from './entries'
@ -8,7 +8,9 @@ export default class AppendActionMenu extends Component {
* @param state
* @return {JSX.Element}
*/
render (props, state) {
render () {
const { props, state} = this
const items = [
createAppend(props.path, props.events.onAppend)
]

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import { findParentNode } from '../../utils/domUtils'
export let CONTEXT_MENU_HEIGHT = 240
@ -19,7 +19,9 @@ export default class Menu extends Component {
* @param state
* @return {*}
*/
render (props, state) {
render () {
const { props, state} = this
if (!props.open) {
return null
}
@ -39,7 +41,7 @@ export default class Menu extends Component {
((orientation === 'top') ? 'jsoneditor-actionmenu-top' : 'jsoneditor-actionmenu-bottom')
return h('div', {
class: className,
className: className,
'data-menu': 'true'
},
props.items.map(this.renderMenuItem)
@ -48,7 +50,7 @@ export default class Menu extends Component {
renderMenuItem = (item, index) => {
if (item.type === 'separator') {
return h('div', {class: 'jsoneditor-menu-separator'})
return h('div', {className: 'jsoneditor-menu-separator'})
}
if (item.click && item.submenu) {
@ -59,24 +61,24 @@ export default class Menu extends Component {
}
// two buttons: direct click and a small button to expand the submenu
return h('div', {class: 'jsoneditor-menu-item'}, [
h('button', {class: 'jsoneditor-menu-button jsoneditor-menu-default ' + item.className, title: item.title, onClick }, [
h('span', {class: 'jsoneditor-icon'}),
h('span', {class: 'jsoneditor-text'}, item.text)
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)
]),
h('button', {class: 'jsoneditor-menu-button jsoneditor-menu-expand', onClick: this.createExpandHandler(index) }, [
h('span', {class: 'jsoneditor-icon jsoneditor-icon-expand'})
h('button', {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', {class: 'jsoneditor-menu-item'}, [
h('button', {class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: this.createExpandHandler(index) }, [
h('span', {class: 'jsoneditor-icon'}),
h('span', {class: 'jsoneditor-text'}, item.text),
h('span', {class: 'jsoneditor-icon jsoneditor-icon-expand'}),
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'}),
]),
this.renderSubMenu(item.submenu, index)
])
@ -89,10 +91,10 @@ export default class Menu extends Component {
}
// just a button (no submenu)
return h('div', {class: 'jsoneditor-menu-item'}, [
h('button', {class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick }, [
h('span', {class: 'jsoneditor-icon'}),
h('span', {class: 'jsoneditor-text'}, item.text)
return h('div', {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)
]),
])
}
@ -113,10 +115,10 @@ export default class Menu extends Component {
this.props.onRequestClose()
}
return h('div', {class: 'jsoneditor-menu-item'}, [
h('button', {class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick }, [
h('span', {class: 'jsoneditor-icon'}),
h('span', {class: 'jsoneditor-text'}, item.text)
return h('div', {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)
]),
])
})
@ -125,7 +127,7 @@ export default class Menu extends Component {
(expanded ? ' jsoneditor-expanded' : '') +
(collapsing ? ' jsoneditor-collapsing' : '')
return h('div', {class: className}, contents)
return h('div', {className: className}, contents)
}
createExpandHandler (index) {

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import ModeMenu from './ModeMenu'
import { toCapital } from '../../utils/stringUtils'
@ -16,8 +16,10 @@ export default class ModeButton extends Component {
* @param state
* @return {*}
*/
render (props, state) {
return h('div', {class: 'jsoneditor-modes'}, [
render () {
const { props, state} = this
return h('div', {className: 'jsoneditor-modes'}, [
h('button', {
title: 'Switch mode',
onClick: this.handleOpen

View File

@ -1,4 +1,4 @@
import { h, Component } from 'preact'
import { createElement as h, Component } from 'react'
import { toCapital } from '../../utils/stringUtils'
import { findParentNode } from '../../utils/domUtils'
@ -8,12 +8,14 @@ export default class ModeMenu extends Component {
* @param {Object} state
* @return {JSX.Element}
*/
render (props, state) {
render () {
const { props, state} = this
if (props.open) {
const items = props.modes.map(mode => {
return h('button', {
title: `Switch to ${mode} mode`,
class: 'jsoneditor-menu-button jsoneditor-type-modes' +
className: 'jsoneditor-menu-button jsoneditor-type-modes' +
((mode === props.mode) ? ' jsoneditor-selected' : ''),
onClick: () => {
try {
@ -28,7 +30,7 @@ export default class ModeMenu extends Component {
})
return h('div', {
class: 'jsoneditor-actionmenu jsoneditor-modemenu',
className: 'jsoneditor-actionmenu jsoneditor-modemenu',
nodemenu: 'true',
}, items)
}

View File

@ -1,4 +1,5 @@
import { h, render } from 'preact'
import { createElement as h, Component } from 'react'
import { render } from 'react-dom'
import CodeMode from './components/CodeMode'
import TextMode from './components/TextMode'
import TreeMode from './components/TreeMode'
@ -136,6 +137,7 @@ function jsoneditor (container, options = {}) {
let success = false
let initialChildCount = editor._container.children.length
let element
let component
try {
// find the constructor for the selected mode
const constructor = editor._modes[mode]
@ -155,7 +157,7 @@ function jsoneditor (container, options = {}) {
}
// create new component
element = render(
component = render(
h(constructor, {
mode,
options: editor._options,
@ -165,11 +167,15 @@ function jsoneditor (container, options = {}) {
// set JSON (this can throw an error)
const text = editor._component ? editor._component.getText() : '{}'
element._component.setText(text)
component.setText(text)
element = editor._container.lastChild
// when setText didn't fail, we will reach this point
success = true
}
catch (err) {
console.error(err)
}
finally {
if (success) {
// destroy previous component
@ -180,7 +186,7 @@ function jsoneditor (container, options = {}) {
editor._mode = mode
editor._element = element
editor._component = element._component
editor._component = component
}
else {
// TODO: fall back to text mode when loading code mode failed?