Skip to content

Commit c6a3336

Browse files
committed
Add Json viewer
1 parent 3c8a76c commit c6a3336

File tree

15 files changed

+347
-36
lines changed

15 files changed

+347
-36
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"monaco-editor-webpack-plugin": "^3.0.1",
4545
"react": "^17.0.2",
4646
"react-dom": "^17.0.2",
47+
"react-json-view": "^1.21.3",
4748
"react-monaco-editor": "^0.43.0",
4849
"react-redux": "^7.2.1",
4950
"redux": "^4.0.5",

src/components/App/index.jsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import React, { Component } from 'react';
2+
23
import Header from '../Header';
34
import Tab from '../Tab';
45
import About from '../About';
6+
import JsonView from '../JsonView';
57

68
import { editorTab, consoleTab, outputTab } from './tabs';
9+
import ContextMenu from '../ContextMenu';
710

811
const MIN_WIDTH = 100;
912
const MAX_WIDTH = 1400;
@@ -17,8 +20,10 @@ class App extends Component {
1720
this.state = {
1821
width: initialWidth,
1922
rightWidth,
23+
position: null,
2024
};
2125
this.resizer = React.createRef();
26+
this.handleContextMenu = this.handleContextMenu.bind(this);
2227
}
2328

2429
componentDidMount() {
@@ -50,8 +55,15 @@ class App extends Component {
5055
window.removeEventListener('mouseup', this.stopResize, false);
5156
};
5257

58+
handleContextMenu = event => {
59+
event.preventDefault();
60+
const { pageX, pageY } = event;
61+
const position = { top: pageY, left: pageX };
62+
this.setState({ position });
63+
};
64+
5365
render() {
54-
const { width, rightWidth } = this.state;
66+
const { width, rightWidth, position } = this.state;
5567

5668
return (
5769
<div className="mainContainer">
@@ -75,13 +87,15 @@ class App extends Component {
7587
<div style={{ minHeight: '50%', height: '50%' }}>
7688
<Tab tabs={outputTab} />
7789
</div>
78-
<div style={{ minHeight: '50%', height: '50%' }}>
90+
<div onContextMenu={this.handleContextMenu} style={{ minHeight: '50%', height: '50%' }}>
7991
<Tab tabs={consoleTab} />
8092
</div>
8193
</div>
8294
</div>
8395

8496
<About />
97+
<JsonView />
98+
<ContextMenu position={position} onClose={() => this.setState({ position: null })} />
8599
</div>
86100
);
87101
}

src/components/Clickable/index.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import './styles.css';
5+
6+
const Clickable = ({ onClick, children }) => {
7+
return (
8+
<button className="clickable" type="button" onClick={onClick} aria-label="clickable">
9+
{children}
10+
</button>
11+
);
12+
};
13+
14+
Clickable.propTypes = {
15+
onClick: PropTypes.func.isRequired,
16+
};
17+
export default Clickable;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.clickable {
2+
background: none;
3+
color: inherit;
4+
border: none;
5+
padding: 0;
6+
font: inherit;
7+
outline: 0;
8+
cursor: pointer;
9+
}

src/components/Console/index.jsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import { connect } from 'react-redux';
4+
import _ from 'lodash';
45

56
const Console = ({ history }) => {
67
const createKey = index => {
@@ -10,17 +11,21 @@ const Console = ({ history }) => {
1011
if (history.length === 0) {
1112
return <></>;
1213
}
14+
1315
return (
1416
<div className="console">
15-
{history.map((item, index) => (
16-
<div key={createKey(index)}>
17-
<pre>
18-
<span style={{ marginRight: 5 }}>&#8250;</span>
17+
{history.map((item, index) => {
18+
const resut = !_.isString(item) ? JSON.stringify(item) : item;
19+
return (
20+
<div key={createKey(index)}>
21+
<pre>
22+
<span style={{ marginRight: 5 }}>&#8250;</span>
1923

20-
<span>{item}</span>
21-
</pre>
22-
</div>
23-
))}
24+
<span>{resut}</span>
25+
</pre>
26+
</div>
27+
);
28+
})}
2429
</div>
2530
);
2631
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import PropTypes from 'prop-types';
4+
import Clickable from '../Clickable';
5+
import { commonActions } from '../../store/actions';
6+
7+
const ContextMenu = ({ position, onClose, dispatch }) => {
8+
if (!position) return null;
9+
10+
const handleFormatClick = () => {
11+
dispatch(commonActions.toggleJsonView());
12+
onClose();
13+
};
14+
return (
15+
<ul
16+
className="menu"
17+
style={{
18+
top: `${position.top}px`,
19+
left: `${position.left}px`,
20+
}}
21+
>
22+
<li>
23+
<Clickable onClick={handleFormatClick}>Fromat JSON</Clickable>
24+
</li>
25+
<li>
26+
<Clickable onClick={onClose}>Close</Clickable>
27+
</li>
28+
</ul>
29+
);
30+
};
31+
32+
ContextMenu.propTypes = {
33+
position: PropTypes.shape({
34+
top: PropTypes.number,
35+
left: PropTypes.number,
36+
}),
37+
onClose: PropTypes.func.isRequired,
38+
};
39+
40+
ContextMenu.defaultProps = {
41+
position: null,
42+
};
43+
44+
// const mapStateToProps = ({ common }) => ({
45+
// position: common.position,
46+
// });
47+
48+
export default connect(null)(ContextMenu);

src/components/Header/code-sample.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
{
1313
"id": 3,
1414
"name": "Axios",
15-
"code": "// axios example. Reference axios in the code as `axios`\n\nconst loadTVShowCast = async () => {\n\tconst tvShowId = 413; // FRIENDS TV Show\n\tconst url = `https://api.tvmaze.com/shows/${tvShowId}/cast`;\n\tconst { data } = await axios.get(url);\n\tconsole.log(data);\n}\n\nloadTVShowCast();"
15+
"code": "// axios example. Reference axios in the code as `axios`\n\nconst loadTVShowCast = async () => {\n\tconst tvShowId = 431; // FRIENDS TV Show\n\tconst url = `https://api.tvmaze.com/shows/${tvShowId}/cast`;\n\tconst { data } = await axios.get(url);\n\tconsole.log(data);\n}\n\nloadTVShowCast();"
1616
},
1717
{
1818
"id": 4,

src/components/JsonView/index.jsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { connect } from 'react-redux';
4+
import ReactJsonView from 'react-json-view';
5+
import { commonActions } from '../../store/actions';
6+
7+
const JsonView = ({ jsonView, dispatch, history }) => {
8+
const close = () => {
9+
dispatch(commonActions.toggleJsonView());
10+
};
11+
12+
return (
13+
<div>
14+
<div className="modal fade show" style={{ display: jsonView }}>
15+
<div className="modal-dialog" style={{ maxWidth: 1200 }}>
16+
<div className="modal-content">
17+
<div className="modal-header">
18+
<h4 className="modal-title">JSON View</h4>
19+
</div>
20+
<div className="modal-body">
21+
<div style={{ height: 600, overflow: 'scroll' }}>
22+
<ReactJsonView src={history} theme="monokai" name={false} />
23+
</div>
24+
</div>
25+
<div className="modal-footer">
26+
<button type="button" className="btn btn-primary" onClick={close}>
27+
Close
28+
</button>
29+
</div>
30+
</div>
31+
</div>
32+
</div>
33+
</div>
34+
);
35+
};
36+
37+
JsonView.propTypes = {
38+
jsonView: PropTypes.string.isRequired,
39+
history: PropTypes.arrayOf(PropTypes.any),
40+
};
41+
42+
JsonView.defaultProps = {
43+
history: [],
44+
};
45+
46+
const mapStateToProps = ({ common }) => ({
47+
jsonView: common.jsonView,
48+
history: common.history.map(item => item),
49+
});
50+
51+
export default connect(mapStateToProps)(JsonView);

src/components/Output/index.jsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import { connect } from 'react-redux';
4+
import _ from 'lodash';
45

56
const Output = ({ error, result }) => {
67
const createKey = index => {
@@ -21,14 +22,17 @@ const Output = ({ error, result }) => {
2122

2223
return (
2324
<div style={{ width: '100%', height: '90%' }} className="console">
24-
{result.map((item, index) => (
25-
<div key={createKey(index)}>
26-
<pre>
27-
<span style={{ marginRight: 5 }}>&#x2023;</span>
28-
<span>{item}</span>
29-
</pre>
30-
</div>
31-
))}
25+
{result.map((item, index) => {
26+
const resut = !_.isString(item) ? JSON.stringify(item) : item;
27+
return (
28+
<div key={createKey(index)}>
29+
<pre>
30+
<span style={{ marginRight: 5 }}>&#x2023;</span>
31+
<span>{resut}</span>
32+
</pre>
33+
</div>
34+
);
35+
})}
3236
</div>
3337
);
3438
};

src/store/actions/common.actions.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ const clearHistory = (withCode = false) => {
99

1010
const toggleModal = () => ({ type: commonTypes.TOGGLE_MODAL });
1111

12+
const toggleJsonView = () => ({ type: commonTypes.TOGGLE_JSON_VIEW });
13+
1214
export const commonActions = {
1315
clearHistory,
1416
toggleModal,
17+
toggleJsonView,
1518
};

0 commit comments

Comments
 (0)