Implemented advanced react example and some minor improvements

This commit is contained in:
jos 2018-08-07 13:32:41 +02:00
parent 983fafdf9c
commit 393dc3f809
18 changed files with 14623 additions and 8 deletions

View File

@ -12,6 +12,7 @@ integrated in React, Vue, and Angular!_
of the editor (expanded nodes, search, selection). This makes it easy to
integrate in frameworks like React.
- Implemented options `onChangeJSON(json)` and `onChangeText(jsonString)`.
- Added two React examples to the `examples` folder.
## 2018-08-02, version 5.19.2

View File

@ -59,7 +59,6 @@
};
editor1.set(json);
editor2.set(json);
</script>
</body>
</html>

21
examples/react_advanced_demo/.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -0,0 +1,21 @@
# JSONEditor React advanced demo
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
## Install
Install dependencies once:
```
npm install
```
## Run
To run the demo:
```
npm start
```
This will open a development server at http://localhost:3000

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
{
"name": "react_advanced_demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"jsoneditor": "file:../..",
"lodash": "4.17.10",
"react": "16.4.2",
"react-dom": "16.4.2",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>JSONEditor | React advanced demo</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,13 @@
.app .contents {
width: 500px;
height: 400px;
}
.app .contents .mode {
padding: 10px 0;
}
.app .contents .code {
background: #f5f5f5;
overflow: auto;
}

View File

@ -0,0 +1,90 @@
import React, { Component } from 'react';
import JSONEditorReact from './JSONEditorReact';
import './App.css';
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
array: {
type: 'array',
items: {
type: 'number'
}
},
boolean: {
type: 'boolean'
},
number: {
type: 'number'
}
},
required: ['array', 'string', 'boolean']
};
const json = {
'array': [1, 2, 3],
'boolean': true,
'null': null,
'number': 'four',
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
const modes = ['tree', 'form', 'view', 'code', 'text'];
class App extends Component {
state = {
schema,
text: JSON.stringify(json, null, 2),
mode: 'tree'
};
render() {
return (
<div className="app">
<h1>JSONEditor React advanced demo</h1>
<div className="contents">
<div className="mode">
mode: <select value={this.state.mode} onChange={this.onModeChangeSelect}>
{
modes.map(mode => <option key={mode} value={mode}>{mode}</option>)
}
</select>
</div>
<JSONEditorReact
schema={this.state.schema}
text={this.state.text}
mode={this.state.mode}
modes={modes}
indentation={4}
onChangeText={this.onChangeText}
onModeChange={this.onModeChange}
/>
<div className="code">
<pre>
<code>
{this.state.text}
</code>
</pre>
</div>
</div>
</div>
);
}
onChangeText = (text) => {
this.setState({ text });
};
onModeChangeSelect = (event) => {
this.setState({ mode: event.target.value });
};
onModeChange = (mode) => {
this.setState({ mode });
};
}
export default App;

View File

@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

View File

@ -0,0 +1,4 @@
.jsoneditor-react-container {
width: 100%;
height: 100%;
}

View File

@ -0,0 +1,65 @@
import React, {Component} from 'react';
import isEqual from 'lodash/isEqual';
import cloneDeep from 'lodash/cloneDeep';
import * as JSONEditor from 'jsoneditor';
import 'jsoneditor/dist/jsoneditor.css';
import './JSONEditorReact.css';
export default class JSONEditorReact extends Component {
componentDidMount () {
// copy all properties into options for the editor
// (except the properties for the JSONEditorReact component itself)
const options = Object.assign({}, this.props);
delete options.json;
delete options.text;
this.jsoneditor = new JSONEditor(this.container, options);
if ('json' in this.props) {
this.jsoneditor.set(this.props.json);
}
if ('text' in this.props) {
this.jsoneditor.setText(this.props.text);
}
this.schema = cloneDeep(this.props.schema);
this.schemaRefs = cloneDeep(this.props.schemaRefs);
}
componentWillUpdate(nextProps, nextState) {
if ('json' in nextProps) {
this.jsoneditor.update(nextProps.json);
}
if ('text' in nextProps) {
this.jsoneditor.updateText(nextProps.text);
}
if ('mode' in nextProps) {
this.jsoneditor.setMode(nextProps.mode);
}
// store a clone of the schema to keep track on when it actually changes.
// (When using a PureComponent all of this would be redundant)
const schemaChanged = !isEqual(nextProps.schema, this.schema);
const schemaRefsChanged = !isEqual(nextProps.schemaRefs, this.schemaRefs);
if (schemaChanged || schemaRefsChanged) {
this.schema = cloneDeep(nextProps.schema);
this.schemaRefs = cloneDeep(nextProps.schemaRefs);
this.jsoneditor.setSchema(nextProps.schema, nextProps.schemaRefs);
}
}
componentWillUnmount () {
if (this.jsoneditor) {
this.jsoneditor.destroy();
}
}
render() {
return (
<div className="jsoneditor-react-container" ref={elem => this.container = elem} />
);
}
}

View File

@ -0,0 +1,3 @@
body {
font-family: sans-serif;
}

View File

@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

View File

@ -23,7 +23,7 @@ export default class JSONEditorDemo extends Component {
}
componentWillUpdate(nextProps, nextState) {
this.jsoneditor.update(nextProps.json)
this.jsoneditor.update(nextProps.json);
}
render() {

View File

@ -11,6 +11,7 @@ table.jsoneditor-search div.jsoneditor-results {
color: white;
padding-right: 5px;
line-height: 24px;
padding-top: 2px;
}
table.jsoneditor-search {

View File

@ -104,7 +104,8 @@ function JSONEditor (container, options, json) {
if (options.onChangeJSON) {
if (options.mode === 'text' || options.mode === 'code' ||
(options.modes && (options.modes.indexOf('text') !== -1 || options.modes.indexOf('code') !== -1))) {
console.warn('Option "onChangeJSON" is not applicable to modes "text" and "code". Use "onChangeText" instead.');
console.warn('Option "onChangeJSON" is not applicable to modes "text" and "code". ' +
'Use "onChangeText" or "onChange" instead.');
}
}
@ -231,6 +232,11 @@ JSONEditor.prototype.getName = function () {
* 'text', and 'code'.
*/
JSONEditor.prototype.setMode = function (mode) {
// if the mode is the same as current mode (and it's not the first time), do nothing.
if (mode === this.options.mode && this.create) {
return;
}
var container = this.container;
var options = util.extend({}, this.options);
var oldMode = options.mode;

View File

@ -322,6 +322,10 @@ textmode.create = function (container, options) {
* @private
*/
textmode._onChange = function () {
if (this.onChangeDisabled) {
return;
}
// validate JSON schema (if configured)
this._debouncedValidate();
@ -626,12 +630,11 @@ textmode.setText = function(jsonText) {
}
if (this.aceEditor) {
// prevent emitting onChange events while setting new text
var originalOnChange = this.options.onChange;
this.options.onChange = null;
this.onChangeDisabled = true;
this.aceEditor.setValue(text, -1);
this.options.onChange = originalOnChange;
this.onChangeDisabled = false;
}
// validate JSON schema
this.validate();
@ -834,18 +837,27 @@ textmode.setTextSelection = function (startPos, endPos) {
}
};
function load () {
try {
this.format()
}
catch (err) {
// in case of an error, just move on, failing formatting is not a big deal
}
}
// define modes
module.exports = [
{
mode: 'text',
mixin: textmode,
data: 'text',
load: textmode.format
load: load
},
{
mode: 'code',
mixin: textmode,
data: 'text',
load: textmode.format
load: load
}
];