diff --git a/README.md b/README.md
index ddbeb3e..df1667a 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,57 @@ The following object will be passed to your method:
Returning `false` from a callback method will prevent the src from being affected.
+### `ReactPureJsonView` component
+
+#### Why is it needed
+
+Users usually use this repository to directly connect and display `static data` (such as external JSON files, other API operation results, etc.). These static data may have (Javascript language) big number ([example](https://www.geeksforgeeks.org/how-to-deal-with-large-numbers-in-javascript/)) and floating point ([example](https://stackoverflow.com/a/55291279)) problems, and users are more interested in correctly displaying the static data and do not care about the data involving different languages (Javascript) problems.
+
+#### Usage
+
+- JSON String Example
+
+ ```js
+ import { ReactPureJsonView } from '@microlink/react-json-view'
+
+ /**
+ * Get `src` (json string) from the external file
+ *
+ * @type {String}
+ */
+ const data = await fetch('data/data.json')
+
+
+ ```
+
+- JSON Object Example (legacy mode, the same as [ReactJsonView Usage](#usage))
+
+ ```js
+ import { ReactPureJsonView } from '@microlink/react-json-view'
+
+
+ ```
+
+#### API
+
+| Name | Type | Default | Description |
+|:-----------------------------|:-------------------------------------------------|:-------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `src` | `JSON Object` or `JSON String` | None | This property contains your input JSON (object or json-string). |
+
### Theming
#### Builtin theme
diff --git a/dev-server/dist/index.html b/dev-server/dist/index.html
index 945bb6e..90116df 100644
--- a/dev-server/dist/index.html
+++ b/dev-server/dist/index.html
@@ -29,6 +29,42 @@
src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"
>
+
+
diff --git a/dev-server/src/JsonString.js b/dev-server/src/JsonString.js
new file mode 100644
index 0000000..06d1bf4
--- /dev/null
+++ b/dev-server/src/JsonString.js
@@ -0,0 +1,200 @@
+'use strict'
+
+//import react and reactDom for browser rendering
+import React from 'react'
+
+// import test json data
+import {
+ getExampleArray,
+ getExampleJson1,
+ getExampleJson2,
+ getExampleJson3,
+ getExampleJson4,
+ getExampleWithStringEscapeSequences
+} from './test.json-data'
+
+//import the react-json-view component (installed with npm)
+import JsonViewer, { defaultBigNumberImplement as BigNumber} from './../../src/js/index'
+
+export default (
+
+ {/* just pass in your JSON to the src attribute */}
+ {
+ console.log('edit callback', e)
+ if (e.new_value == 'error') {
+ return false
+ }
+ }}
+ onDelete={e => {
+ console.log('delete callback', e)
+ }}
+ onAdd={e => {
+ console.log('add callback', e)
+ if (e.new_value == 'error') {
+ return false
+ }
+ }}
+ onSelect={e => {
+ console.log('select callback', e)
+ console.log(e.namespace)
+ }}
+ displayObjectSize={true}
+ name={'dev-server'}
+ enableClipboard={copy => {
+ console.log('you copied to clipboard!', copy)
+ }}
+ shouldCollapse={({ src, namespace, type }) => {
+ if (type === 'array' && src.indexOf('test') > -1) {
+ return true
+ } else if (namespace.indexOf('moment') > -1) {
+ return true
+ }
+ return false
+ }}
+ defaultValue=''
+ />
+
+
+
+ {/* use a base16 theme */}
+ {
+ console.log(e)
+ if (e.new_value === 'error') {
+ return false
+ }
+ }}
+ onDelete={e => {
+ console.log(e)
+ }}
+ onAdd={e => {
+ console.log(e)
+ if (e.new_value === 'error') {
+ return false
+ }
+ }}
+ name={false}
+ iconStyle='triangle'
+ shouldCollapse={({ src, type }) =>
+ type === 'object' &&
+ src.constructor &&
+ src.constructor.name === 'Moment'
+ }
+ selectOnFocus
+ />
+
+
+
+ {/* initialize this one with a name and default collapsed */}
+
+
+
+
+ {/* initialize this one with a name and default collapsed */}
+
+
+
+
+ {/* initialize an example with a long string */}
+
+
+
+
+ {/*demo array support*/}
+ {
+ console.log(edit)
+ }}
+ />
+
+
+
+ {/* custom theme example */}
+
+ namespace.indexOf('moment') > -1
+ }
+ theme={{
+ base00: 'white',
+ base01: '#ddd',
+ base02: '#ddd',
+ base03: '#444',
+ base04: 'purple',
+ base05: '#444',
+ base06: '#444',
+ base07: '#444',
+ base08: '#444',
+ base09: 'rgba(70, 70, 230, 1)',
+ base0A: 'rgba(70, 70, 230, 1)',
+ base0B: 'rgba(70, 70, 230, 1)',
+ base0C: 'rgba(70, 70, 230, 1)',
+ base0D: 'rgba(70, 70, 230, 1)',
+ base0E: 'rgba(70, 70, 230, 1)',
+ base0F: 'rgba(70, 70, 230, 1)'
+ }}
+ />
+
+
+
+ {/* Name as colored react component */}
+
+ React Element as name
+
+ }
+ src={getExampleJson2()}
+ />
+
+ {/* String with special escape sequences */}
+
+
+)
diff --git a/dev-server/src/index.js b/dev-server/src/index.js
index 229ae0e..4caf8a8 100644
--- a/dev-server/src/index.js
+++ b/dev-server/src/index.js
@@ -2,321 +2,35 @@
//import react and reactDom for browser rendering
import React from 'react'
-import ReactDom from 'react-dom'
-import Moment from 'moment'
+import render from './render'
//import the react-json-view component (installed with npm)
-import JsonViewer from './../../src/js/index'
+import JsonViewer, {
+ ReactPureJsonView as PureJsonView
+} from './../../src/js/index'
-// custom big number class, You can use existing libraries like `bignumber.js`, `decimal.js`, `big.js` etc.
-class BigNumber {
- name = 'customName'
- constructor(value) {
- this.value = value
- }
- toString() {
- return this.value.toString()
- }
-}
-
-//render 2 different examples of the react-json-view component
-ReactDom.render(
-
- {/* just pass in your JSON to the src attribute */}
- {
- console.log('edit callback', e)
- if (e.new_value == 'error') {
- return false
- }
- }}
- onDelete={e => {
- console.log('delete callback', e)
- }}
- onAdd={e => {
- console.log('add callback', e)
- if (e.new_value == 'error') {
- return false
- }
- }}
- onSelect={e => {
- console.log('select callback', e)
- console.log(e.namespace)
- }}
- displayObjectSize={true}
- name={'dev-server'}
- enableClipboard={copy => {
- console.log('you copied to clipboard!', copy)
- }}
- shouldCollapse={({ src, namespace, type }) => {
- if (type === 'array' && src.indexOf('test') > -1) {
- return true
- } else if (namespace.indexOf('moment') > -1) {
- return true
- }
- return false
- }}
- defaultValue=''
- />
-
-
-
- {/* use a base16 theme */}
- {
- console.log(e)
- if (e.new_value === 'error') {
- return false
- }
- }}
- onDelete={e => {
- console.log(e)
- }}
- onAdd={e => {
- console.log(e)
- if (e.new_value === 'error') {
- return false
- }
- }}
- name={false}
- iconStyle='triangle'
- shouldCollapse={({ src, type }) =>
- type === 'object' &&
- src.constructor &&
- src.constructor.name === 'Moment'
- }
- selectOnFocus
- />
-
-
-
- {/* initialize this one with a name and default collapsed */}
-
-
-
-
- {/* initialize this one with a name and default collapsed */}
-
-
-
-
- {/* initialize an example with a long string */}
-
-
-
-
- {/*demo array support*/}
- {
- console.log(edit)
- }}
- />
-
-
-
- {/* custom theme example */}
-
- namespace.indexOf('moment') > -1
- }
- theme={{
- base00: 'white',
- base01: '#ddd',
- base02: '#ddd',
- base03: '#444',
- base04: 'purple',
- base05: '#444',
- base06: '#444',
- base07: '#444',
- base08: '#444',
- base09: 'rgba(70, 70, 230, 1)',
- base0A: 'rgba(70, 70, 230, 1)',
- base0B: 'rgba(70, 70, 230, 1)',
- base0C: 'rgba(70, 70, 230, 1)',
- base0D: 'rgba(70, 70, 230, 1)',
- base0E: 'rgba(70, 70, 230, 1)',
- base0F: 'rgba(70, 70, 230, 1)'
- }}
- />
+// import test json data
+import * as defaultTestJsonData from './object.test-data'
-
+import { exported as stringTestJsonData } from './string.test-data'
- {/* Name as colored react component */}
-
- React Element as name
-
- }
- src={getExampleJson2()}
- />
+const search = new URLSearchParams(location.search)
+const json_string = search.get('json_string')
- {/* String with special escape sequences */}
-
-
,
- document.getElementById('app-container')
-)
+const appTitle = document.querySelector('#app-title')
-/*-------------------------------------------------------------------------*/
-/* the following functions just contain test json data for display */
-/*-------------------------------------------------------------------------*/
-
-//just a function to get an example JSON object
-function getExampleJson1 () {
- return {
- string: 'this is a test string',
- integer: 42,
- empty_array: [],
- empty_object: {},
- array: [1, 2, 3, 'test'],
- float: -2.757,
- undefined_var: undefined,
- parent: {
- sibling1: true,
- sibling2: false,
- sibling3: null,
- isString: value => {
- if (typeof value === 'string') {
- return 'string'
- } else {
- return 'other'
- }
- }
- },
- string_number: '1234',
- date: new Date(),
- moment: Moment(),
- regexp: /[0-9]/gi,
- bigNumber: new BigNumber('0.0060254656709730629123')
- }
-}
-
-//and another a function to get an example JSON object
-function getExampleJson2 () {
- return {
- normalized: {
- '1-grams': {
- body: 1,
- testing: 1
- },
- '2-grams': {
- 'testing body': 1
- },
- '3-grams': {}
- },
- noun_phrases: {
- body: 1
- },
- lemmatized: {
- '1-grams': {
- test: 1,
- body: 1
- },
- '2-grams': {
- 'test body': 1
- },
- '3-grams': {}
- },
- dependency: {
- '1-grams': {
- testingVERBROOTtestingVERB: 1,
- bodyNOUNdobjtestingVERB: 1
- },
- '2-grams': {
- 'testingVERBROOTtestingVERB bodyNOUNdobjtestingVERB': 1
- },
- '3-grams': {}
- }
+if (json_string) {
+ if (json_string === 'true') {
+ appTitle.textContent = 'ReactPureJsonView: json string for prop.src'
+ render(stringTestJsonData, PureJsonView, null)
+ } else if (json_string === 'false') {
+ appTitle.textContent = 'ReactPureJsonView: json object/array for prop.src'
+ render(defaultTestJsonData, PureJsonView, null)
+ } else {
+ console.error('invalid option')
}
-}
-
-function getExampleJson3 () {
- return {
- example_information:
- 'this example has the collapsed prop set to true and the indentWidth prop is set to 8',
- default_collapsed: true,
- collapsed_array: [
- 'you expanded me',
- 'try collapsing and expanding the root node',
- 'i will still be expanded',
- {
- leaf_node: true
- }
- ]
- }
-}
-
-function getExampleJson4 () {
- const large_array = new Array(225).fill('this is a large array full of items')
-
- large_array.push(getExampleArray())
-
- large_array.push(new Array(75).fill(Math.random()))
-
- return large_array
-}
-
-function getExampleArray () {
- return [
- 'you can also display arrays!',
- new Date(),
- 1,
- 2,
- 3,
- {
- pretty_cool: true
- }
- ]
-}
-
-function getExampleWithStringEscapeSequences () {
- return { '\\\n\t\r\f\\n': '\\\n\t\r\f\\n' }
+} else {
+ appTitle.textContent = 'ReactJsonView: json object/array for prop.src'
+ render(defaultTestJsonData, JsonViewer)
}
diff --git a/dev-server/src/object.test-data.js b/dev-server/src/object.test-data.js
new file mode 100644
index 0000000..16ef175
--- /dev/null
+++ b/dev-server/src/object.test-data.js
@@ -0,0 +1,120 @@
+'use strict'
+
+/*-------------------------------------------------------------------------*/
+/* the following functions just contain test json data for display */
+/*-------------------------------------------------------------------------*/
+
+import Moment from 'moment'
+import { defaultBigNumberImplement as BigNumber} from './../../src/js/index'
+
+//just a function to get an example JSON object
+export function getExampleJson1 () {
+ return {
+ string: 'this is a test string',
+ integer: 42,
+ empty_array: [],
+ empty_object: {},
+ array: [1, 2, 3, 'test'],
+ float: -2.757,
+ undefined_var: undefined,
+ parent: {
+ sibling1: true,
+ sibling2: false,
+ sibling3: null,
+ isString: value => {
+ if (typeof value === 'string') {
+ return 'string'
+ } else {
+ return 'other'
+ }
+ }
+ },
+ string_number: '1234',
+ date: new Date(),
+ moment: Moment(),
+ regexp: /[0-9]/gi,
+ bigNumber: new BigNumber('0.0060254656709730629123')
+ }
+}
+
+//and another a function to get an example JSON object
+export function getExampleJson2 () {
+ return {
+ normalized: {
+ '1-grams': {
+ body: 1,
+ testing: 1
+ },
+ '2-grams': {
+ 'testing body': 1
+ },
+ '3-grams': {}
+ },
+ noun_phrases: {
+ body: 1
+ },
+ lemmatized: {
+ '1-grams': {
+ test: 1,
+ body: 1
+ },
+ '2-grams': {
+ 'test body': 1
+ },
+ '3-grams': {}
+ },
+ dependency: {
+ '1-grams': {
+ testingVERBROOTtestingVERB: 1,
+ bodyNOUNdobjtestingVERB: 1
+ },
+ '2-grams': {
+ 'testingVERBROOTtestingVERB bodyNOUNdobjtestingVERB': 1
+ },
+ '3-grams': {}
+ }
+ }
+}
+
+export function getExampleJson3 () {
+ return {
+ example_information:
+ 'this example has the collapsed prop set to true and the indentWidth prop is set to 8',
+ default_collapsed: true,
+ collapsed_array: [
+ 'you expanded me',
+ 'try collapsing and expanding the root node',
+ 'i will still be expanded',
+ {
+ leaf_node: true
+ }
+ ]
+ }
+}
+
+export function getExampleJson4 () {
+ const large_array = new Array(225).fill('this is a large array full of items')
+
+ large_array.push(getExampleArray())
+
+ large_array.push(new Array(75).fill(Math.random()))
+
+ return large_array
+}
+
+export function getExampleArray () {
+ return [
+ 'you can also display arrays!',
+ new Date(),
+ 1,
+ 2,
+ 3,
+ {
+ pretty_cool: true
+ }
+ ]
+}
+
+export function getExampleWithStringEscapeSequences () {
+ return { '\\\n\t\r\f\\n': '\\\n\t\r\f\\n' }
+}
diff --git a/dev-server/src/render.js b/dev-server/src/render.js
new file mode 100644
index 0000000..8253807
--- /dev/null
+++ b/dev-server/src/render.js
@@ -0,0 +1,236 @@
+'use strict'
+
+//import react and reactDom for browser rendering
+import React from 'react'
+import ReactDom from 'react-dom'
+
+import {
+ ReactPureJsonView,
+ defaultBigNumberImplement
+} from './../../src/js/index'
+
+const render = (
+ {
+ getExampleArray,
+ getExampleJson1,
+ getExampleJson2,
+ getExampleJson3,
+ getExampleJson4,
+ getExampleWithStringEscapeSequences,
+
+ __isStringifyDataGetter__: isStringifyDataGetter
+ },
+ JsonViewer,
+ bigNumber = defaultBigNumberImplement
+) => {
+ const specialProps = {
+ ...(bigNumber ? { bigNumber } : undefined)
+ }
+
+ const specialRender =
+ ReactPureJsonView === JsonViewer && isStringifyDataGetter ? (
+
+
+`}>
+
+
+
+
+ ) : null
+
+ //render 2 different examples of the react-json-view component
+ ReactDom.render(
+
+ {specialRender}
+
+ {/* just pass in your JSON to the src attribute */}
+ {
+ console.log('edit callback', e)
+ if (e.new_value == 'error') {
+ return false
+ }
+ }}
+ onDelete={e => {
+ console.log('delete callback', e)
+ }}
+ onAdd={e => {
+ console.log('add callback', e)
+ if (e.new_value == 'error') {
+ return false
+ }
+ }}
+ onSelect={e => {
+ console.log('select callback', e)
+ console.log(e.namespace)
+ }}
+ displayObjectSize={true}
+ name={'dev-server'}
+ enableClipboard={copy => {
+ console.log('you copied to clipboard!', copy)
+ }}
+ shouldCollapse={({ src, namespace, type }) => {
+ if (type === 'array' && src.indexOf('test') > -1) {
+ return true
+ } else if (namespace.indexOf('moment') > -1) {
+ return true
+ }
+ return false
+ }}
+ defaultValue=''
+ />
+
+
+
+ {/* use a base16 theme */}
+ {
+ console.log(e)
+ if (e.new_value === 'error') {
+ return false
+ }
+ }}
+ onDelete={e => {
+ console.log(e)
+ }}
+ onAdd={e => {
+ console.log(e)
+ if (e.new_value === 'error') {
+ return false
+ }
+ }}
+ name={false}
+ iconStyle='triangle'
+ shouldCollapse={({ src, type }) =>
+ type === 'object' &&
+ src.constructor &&
+ src.constructor.name === 'Moment'
+ }
+ selectOnFocus
+ />
+
+
+
+ {/* initialize this one with a name and default collapsed */}
+
+
+
+
+ {/* initialize this one with a name and default collapsed */}
+
+
+
+
+ {/* initialize an example with a long string */}
+
+
+
+
+ {/*demo array support*/}
+ {
+ console.log(edit)
+ }}
+ />
+
+
+
+ {/* custom theme example */}
+
+ namespace.indexOf('moment') > -1
+ }
+ theme={{
+ base00: 'white',
+ base01: '#ddd',
+ base02: '#ddd',
+ base03: '#444',
+ base04: 'purple',
+ base05: '#444',
+ base06: '#444',
+ base07: '#444',
+ base08: '#444',
+ base09: 'rgba(70, 70, 230, 1)',
+ base0A: 'rgba(70, 70, 230, 1)',
+ base0B: 'rgba(70, 70, 230, 1)',
+ base0C: 'rgba(70, 70, 230, 1)',
+ base0D: 'rgba(70, 70, 230, 1)',
+ base0E: 'rgba(70, 70, 230, 1)',
+ base0F: 'rgba(70, 70, 230, 1)'
+ }}
+ />
+
+
+
+ {/* Name as colored react component */}
+
+ React Element as name
+
+ }
+ src={getExampleJson2()}
+ />
+
+ {/* String with special escape sequences */}
+
+
,
+ document.getElementById('app-container')
+ )
+}
+
+export default render
diff --git a/dev-server/src/string.test-data.js b/dev-server/src/string.test-data.js
new file mode 100644
index 0000000..d9d76ed
--- /dev/null
+++ b/dev-server/src/string.test-data.js
@@ -0,0 +1,21 @@
+'use strict'
+
+import { defaultJSONImplement as JSONBig } from './../../src/js/index'
+
+// import test json data
+import * as defaultTestJsonData from './object.test-data'
+
+const exported = {
+ __isStringifyDataGetter__: true
+}
+
+Object.keys(defaultTestJsonData).forEach(key => {
+ exported[key] = (...args) => {
+ const data = defaultTestJsonData[key](...args)
+
+ // Convert to json string, will throw the function which is not supported.
+ return JSONBig.stringify(data)
+ }
+})
+
+export { exported }
diff --git a/index.d.ts b/index.d.ts
index 3d49b19..9ecb416 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -167,6 +167,10 @@ export interface ReactJsonViewProps {
escapeStrings?: boolean
}
+export interface ReactPureJsonViewProps extends ReactJsonViewProps {
+ src: ReactJsonViewProps['src'] | string
+}
+
export interface OnCopyProps {
/**
* The JSON tree source object
@@ -310,3 +314,9 @@ export type ThemeKeys =
declare const ReactJson: React.ComponentType
export default ReactJson
+
+declare const defaultBigNumberImplement: typeof import('bignumber.js').BigNumber
+declare const defaultJSONImplement: typeof import('json-bigint')
+declare const ReactPureJsonView: React.ComponentType
+
+export { ReactPureJsonView, defaultBigNumberImplement, defaultJSONImplement }
diff --git a/package.json b/package.json
index 8a56e40..b8ed3a3 100644
--- a/package.json
+++ b/package.json
@@ -207,6 +207,8 @@
"treeview"
],
"dependencies": {
+ "bignumber.js": "~9.1.2",
+ "json-bigint": "~1.0.0",
"react-base16-styling": "~0.9.0",
"react-lifecycles-compat": "~3.0.4",
"react-textarea-autosize": "~8.5.7"
diff --git a/src/js/ReactJsonView.js b/src/js/ReactJsonView.js
new file mode 100644
index 0000000..0e48d17
--- /dev/null
+++ b/src/js/ReactJsonView.js
@@ -0,0 +1,272 @@
+import React from 'react'
+import { polyfill } from 'react-lifecycles-compat'
+import JsonViewer from './components/JsonViewer'
+import AddKeyRequest from './components/ObjectKeyModal/AddKeyRequest'
+import ValidationFailure from './components/ValidationFailure'
+import { toType, isTheme } from './helpers/util'
+import ObjectAttributes from './stores/ObjectAttributes'
+
+// global theme
+import Theme from './themes/getStyle'
+
+// forward src through to JsonObject component
+class ReactJsonView extends React.PureComponent {
+ constructor (props) {
+ super(props)
+ this.state = {
+ // listen to request to add/edit a key to an object
+ addKeyRequest: false,
+ editKeyRequest: false,
+ validationFailure: false,
+ src: ReactJsonView.defaultProps.src,
+ name: ReactJsonView.defaultProps.name,
+ theme: ReactJsonView.defaultProps.theme,
+ validationMessage: ReactJsonView.defaultProps.validationMessage,
+ // the state object also needs to remember the prev prop values, because we need to compare
+ // old and new props in getDerivedStateFromProps().
+ prevSrc: ReactJsonView.defaultProps.src,
+ prevName: ReactJsonView.defaultProps.name,
+ prevTheme: ReactJsonView.defaultProps.theme
+ }
+ }
+
+ // reference id for this instance
+ rjvId = Date.now().toString()
+
+ // all acceptable props and default values
+ static defaultProps = {
+ src: {},
+ name: 'root',
+ theme: 'rjv-default',
+ collapsed: false,
+ collapseStringsAfterLength: false,
+ shouldCollapse: false,
+ sortKeys: false,
+ quotesOnKeys: true,
+ groupArraysAfterLength: 100,
+ indentWidth: 4,
+ enableClipboard: true,
+ escapeStrings: true,
+ displayObjectSize: true,
+ displayDataTypes: true,
+ onEdit: false,
+ onDelete: false,
+ onAdd: false,
+ onSelect: false,
+ iconStyle: 'triangle',
+ style: {},
+ validationMessage: 'Validation Error',
+ defaultValue: null,
+ displayArrayKey: true,
+ selectOnFocus: false,
+ keyModifier: e => e.metaKey || e.ctrlKey,
+ bigNumber: null
+ }
+
+ // will trigger whenever setState() is called, or parent passes in new props.
+ static getDerivedStateFromProps (nextProps, prevState) {
+ if (
+ nextProps.src !== prevState.prevSrc ||
+ nextProps.name !== prevState.prevName ||
+ nextProps.theme !== prevState.prevTheme
+ ) {
+ // if we pass in new props, we re-validate
+ const newPartialState = {
+ src: nextProps.src,
+ name: nextProps.name,
+ theme: nextProps.theme,
+ validationMessage: nextProps.validationMessage,
+ prevSrc: nextProps.src,
+ prevName: nextProps.name,
+ prevTheme: nextProps.theme
+ }
+ return ReactJsonView.validateState(newPartialState)
+ }
+ return null
+ }
+
+ componentDidMount () {
+ // initialize
+ ObjectAttributes.set(this.rjvId, 'global', 'src', this.state.src)
+ // bind to events
+ const listeners = this.getListeners()
+ for (const i in listeners) {
+ ObjectAttributes.on(i + '-' + this.rjvId, listeners[i])
+ }
+ // reset key request to false once it's observed
+ this.setState({
+ addKeyRequest: false,
+ editKeyRequest: false
+ })
+ }
+
+ componentDidUpdate (prevProps, prevState) {
+ // reset key request to false once it's observed
+ if (prevState.addKeyRequest !== false) {
+ this.setState({
+ addKeyRequest: false
+ })
+ }
+ if (prevState.editKeyRequest !== false) {
+ this.setState({
+ editKeyRequest: false
+ })
+ }
+ if (prevProps.src !== this.state.src) {
+ ObjectAttributes.set(this.rjvId, 'global', 'src', this.state.src)
+ }
+ }
+
+ componentWillUnmount () {
+ const listeners = this.getListeners()
+ for (const i in listeners) {
+ ObjectAttributes.removeListener(i + '-' + this.rjvId, listeners[i])
+ }
+ }
+
+ getListeners = () => {
+ return {
+ reset: this.resetState,
+ 'variable-update': this.updateSrc,
+ 'add-key-request': this.addKeyRequest
+ }
+ }
+
+ // make sure props are passed in as expected
+ static validateState = state => {
+ const validatedState = {}
+ // make sure theme is valid
+ if (toType(state.theme) === 'object' && !isTheme(state.theme)) {
+ console.error(
+ 'react-json-view error:',
+ 'theme prop must be a theme name or valid base-16 theme object.',
+ 'defaulting to "rjv-default" theme'
+ )
+ validatedState.theme = 'rjv-default'
+ }
+ // make sure `src` prop is valid
+ if (toType(state.src) !== 'object' && toType(state.src) !== 'array') {
+ console.error(
+ 'react-json-view error:',
+ 'src property must be a valid json object'
+ )
+ validatedState.name = 'ERROR'
+ validatedState.src = {
+ message: 'src property must be a valid json object'
+ }
+ }
+ return {
+ // get the original state
+ ...state,
+ // override the original state
+ ...validatedState
+ }
+ }
+
+ render () {
+ const {
+ validationFailure,
+ validationMessage,
+ addKeyRequest,
+ theme,
+ src,
+ name
+ } = this.state
+
+ const { style, defaultValue } = this.props
+
+ return (
+
+ )
+ }
+
+ updateSrc = () => {
+ const {
+ name,
+ namespace,
+ new_value,
+ existing_value,
+ variable_removed,
+ updated_src,
+ type
+ } = ObjectAttributes.get(this.rjvId, 'action', 'variable-update')
+ const { onEdit, onDelete, onAdd } = this.props
+
+ const { src } = this.state
+
+ let result
+
+ const on_edit_payload = {
+ existing_src: src,
+ new_value,
+ updated_src,
+ name,
+ namespace,
+ existing_value
+ }
+
+ switch (type) {
+ case 'variable-added':
+ result = onAdd(on_edit_payload)
+ break
+ case 'variable-edited':
+ result = onEdit(on_edit_payload)
+ break
+ case 'variable-removed':
+ result = onDelete(on_edit_payload)
+ break
+ }
+
+ if (result !== false) {
+ ObjectAttributes.set(this.rjvId, 'global', 'src', updated_src)
+ this.setState({
+ src: updated_src
+ })
+ } else {
+ this.setState({
+ validationFailure: true
+ })
+ }
+ }
+
+ addKeyRequest = () => {
+ this.setState({
+ addKeyRequest: true
+ })
+ }
+
+ resetState = () => {
+ this.setState({
+ validationFailure: false,
+ addKeyRequest: false
+ })
+ }
+}
+
+polyfill(ReactJsonView)
+
+export default ReactJsonView
diff --git a/src/js/ReactPureJsonView.js b/src/js/ReactPureJsonView.js
new file mode 100644
index 0000000..24d7851
--- /dev/null
+++ b/src/js/ReactPureJsonView.js
@@ -0,0 +1,84 @@
+import React from 'react'
+import { polyfill } from 'react-lifecycles-compat'
+
+import {
+ getEnsureSrc,
+ defaultBigNumberImplement,
+ defaultJSONImplement
+} from './helpers/bigNumberUtil'
+import ReactJsonView from './ReactJsonView'
+
+// Support **json** or **json string** data as `prop.src` value, and also has built-in support
+// for big-number and higher precision float. Using to present the pure data (json file or json reponse) in recommend,
+// but also support json object/array.
+//
+// Why this component should be included in recommend ?
+// - Today, the data is easy to obtain and rich. It is likely to contain big number and higher precision float.
+// Some data comes from golang or python program, and maybe the data validity exceeds the normal situation of javascript.
+// Automatically handle big number and precision issues, bringing convenience to general users and reducing their psychological burden.
+// This component will let `out of box` in your hand, and you donot handle javascript's big number or float precision problem.
+//
+// A). Use ReactJsonView to render data which will include big number.
+// ```jsx
+// const data = await fetch("data/camera_fov30.json")
+// const src = JSONBig.parse(data) // Use `json-bigint`
+//
+// // Use `bignumber.js`
+// return
+// ```
+//
+// B). Use ReactPureJsonView to render data which will include big number.
+// ```jsx
+// const data = await fetch("data/camera_fov30.json")
+// return
+// ```
+//
+// Which one is better? It's obvious!
+//
+// Refer
+// [A] - [float problem](https://stackoverflow.com/questions/55280847/floating-point-number-in-javascript-ieee-754)
+// [B] - [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
+class ReactPureJsonView extends React.PureComponent {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ src: ReactPureJsonView.defaultProps.src,
+ prevSrc: ReactPureJsonView.defaultProps.src
+ }
+ }
+
+ // will trigger whenever setState() is called, or parent passes in new props.
+ static getDerivedStateFromProps (nextProps, prevState) {
+ const nextSrc = getEnsureSrc(nextProps.src, defaultJSONImplement)
+
+ if (nextSrc !== prevState.prevSrc) {
+ const newPartialState = {
+ src: nextSrc,
+ prevSrc: nextSrc
+ }
+
+ return newPartialState
+ }
+ }
+
+ static defaultProps = {
+ src: {}
+ }
+
+ render () {
+ const props = {
+ ...this.props,
+ src: this.state.src,
+ bigNumber: defaultBigNumberImplement
+ }
+
+ return
+ }
+}
+
+polyfill(ReactPureJsonView)
+
+export default ReactPureJsonView
+
+export { defaultBigNumberImplement, defaultJSONImplement }
diff --git a/src/js/helpers/bigNumberUtil.js b/src/js/helpers/bigNumberUtil.js
new file mode 100644
index 0000000..3634f11
--- /dev/null
+++ b/src/js/helpers/bigNumberUtil.js
@@ -0,0 +1,69 @@
+import JSONBig from 'json-bigint'
+
+// Avoid: `import bigNumberImpl from 'bignumber.js'`
+const bigNumberImpl = require('bignumber.js')
+
+import { toType } from './util'
+
+/**
+ *
+ * Refer https://github.com/sidorares/json-bigint?tab=readme-ov-file#json-bigint
+ * ```
+ * JSON.parse/stringify with bigints support. Based on
+ * Douglas Crockford JSON.js package and bignumber.js library.
+ * ``
+ *
+ * @todo support the specified implement
+ */
+const defaultBigNumberImplement = bigNumberImpl
+const defaultJSONImplement = JSONBig
+
+export { defaultJSONImplement, defaultBigNumberImplement }
+
+function getEnsureSrcDataType (src) {
+ const srcType = src === null || src === undefined ? undefined : toType(src)
+ const validSrcTypes = ['string', 'object', 'array']
+
+ if (validSrcTypes.find(type => type == srcType)) {
+ return srcType
+ }
+
+ return undefined
+}
+
+// Only array or object or error-message's object will be return
+export function getEnsureSrc (src, JSONImpl = defaultJSONImplement) {
+ const type = getEnsureSrcDataType(src)
+
+ // type should json string or json object
+ if (type === undefined) {
+ const errorMsg = 'src property must be a valid json string or json object'
+ console.error('react-json-view error:', errorMsg)
+
+ // error object to warn
+ src = {
+ message: errorMsg
+ }
+ } else if (type === 'string') {
+ const errorMsg = 'only support array-json string or object-json string'
+ try {
+ const parsedSrc = JSONImpl.parse(src)
+ const type = getEnsureSrcDataType(parsedSrc)
+
+ if (type !== 'object' && type !== 'array') {
+ throw new Error(errorMsg)
+ }
+
+ return parsedSrc
+ } catch (err) {
+ console.error('react-json-view error:', errorMsg)
+
+ // error object to warn
+ src = {
+ message: errorMsg
+ }
+ }
+ }
+
+ return src
+}
diff --git a/src/js/index.js b/src/js/index.js
index 538bb68..5c7486f 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -1,271 +1,14 @@
-import React from 'react'
-import { polyfill } from 'react-lifecycles-compat'
-import JsonViewer from './components/JsonViewer'
-import AddKeyRequest from './components/ObjectKeyModal/AddKeyRequest'
-import ValidationFailure from './components/ValidationFailure'
-import { toType, isTheme } from './helpers/util'
-import ObjectAttributes from './stores/ObjectAttributes'
+import ReactJsonView from './ReactJsonView'
+import ReactPureJsonView, {
+ defaultBigNumberImplement,
+ defaultJSONImplement
+} from './ReactPureJsonView'
-// global theme
-import Theme from './themes/getStyle'
-
-// forward src through to JsonObject component
-class ReactJsonView extends React.PureComponent {
- constructor (props) {
- super(props)
- this.state = {
- // listen to request to add/edit a key to an object
- addKeyRequest: false,
- editKeyRequest: false,
- validationFailure: false,
- src: ReactJsonView.defaultProps.src,
- name: ReactJsonView.defaultProps.name,
- theme: ReactJsonView.defaultProps.theme,
- validationMessage: ReactJsonView.defaultProps.validationMessage,
- // the state object also needs to remember the prev prop values, because we need to compare
- // old and new props in getDerivedStateFromProps().
- prevSrc: ReactJsonView.defaultProps.src,
- prevName: ReactJsonView.defaultProps.name,
- prevTheme: ReactJsonView.defaultProps.theme
- }
- }
-
- // reference id for this instance
- rjvId = Date.now().toString() + Math.random().toString(36).slice(2)
-
- // all acceptable props and default values
- static defaultProps = {
- src: {},
- name: 'root',
- theme: 'rjv-default',
- collapsed: false,
- collapseStringsAfterLength: false,
- shouldCollapse: false,
- sortKeys: false,
- quotesOnKeys: true,
- groupArraysAfterLength: 100,
- indentWidth: 4,
- enableClipboard: true,
- escapeStrings: true,
- displayObjectSize: true,
- displayDataTypes: true,
- onEdit: false,
- onDelete: false,
- onAdd: false,
- onSelect: false,
- iconStyle: 'triangle',
- style: {},
- validationMessage: 'Validation Error',
- defaultValue: null,
- displayArrayKey: true,
- selectOnFocus: false,
- keyModifier: e => e.metaKey || e.ctrlKey,
- bigNumber: null
- }
-
- // will trigger whenever setState() is called, or parent passes in new props.
- static getDerivedStateFromProps (nextProps, prevState) {
- if (
- nextProps.src !== prevState.prevSrc ||
- nextProps.name !== prevState.prevName ||
- nextProps.theme !== prevState.prevTheme
- ) {
- // if we pass in new props, we re-validate
- const newPartialState = {
- src: nextProps.src,
- name: nextProps.name,
- theme: nextProps.theme,
- validationMessage: nextProps.validationMessage,
- prevSrc: nextProps.src,
- prevName: nextProps.name,
- prevTheme: nextProps.theme
- }
- return ReactJsonView.validateState(newPartialState)
- }
- return null
- }
-
- componentDidMount () {
- // initialize
- ObjectAttributes.set(this.rjvId, 'global', 'src', this.state.src)
- // bind to events
- const listeners = this.getListeners()
- for (const i in listeners) {
- ObjectAttributes.on(i + '-' + this.rjvId, listeners[i])
- }
- // reset key request to false once it's observed
- this.setState({
- addKeyRequest: false,
- editKeyRequest: false
- })
- }
-
- componentDidUpdate (prevProps, prevState) {
- // reset key request to false once it's observed
- if (prevState.addKeyRequest !== false) {
- this.setState({
- addKeyRequest: false
- })
- }
- if (prevState.editKeyRequest !== false) {
- this.setState({
- editKeyRequest: false
- })
- }
- if (prevProps.src !== this.state.src) {
- ObjectAttributes.set(this.rjvId, 'global', 'src', this.state.src)
- }
- }
-
- componentWillUnmount () {
- const listeners = this.getListeners()
- for (const i in listeners) {
- ObjectAttributes.removeListener(i + '-' + this.rjvId, listeners[i])
- }
- }
-
- getListeners = () => {
- return {
- reset: this.resetState,
- 'variable-update': this.updateSrc,
- 'add-key-request': this.addKeyRequest
- }
- }
-
- // make sure props are passed in as expected
- static validateState = state => {
- const validatedState = {}
- // make sure theme is valid
- if (toType(state.theme) === 'object' && !isTheme(state.theme)) {
- console.error(
- 'react-json-view error:',
- 'theme prop must be a theme name or valid base-16 theme object.',
- 'defaulting to "rjv-default" theme'
- )
- validatedState.theme = 'rjv-default'
- }
- // make sure `src` prop is valid
- if (toType(state.src) !== 'object' && toType(state.src) !== 'array') {
- console.error(
- 'react-json-view error:',
- 'src property must be a valid json object'
- )
- validatedState.name = 'ERROR'
- validatedState.src = {
- message: 'src property must be a valid json object'
- }
- }
- return {
- // get the original state
- ...state,
- // override the original state
- ...validatedState
- }
- }
-
- render () {
- const {
- validationFailure,
- validationMessage,
- addKeyRequest,
- theme,
- src,
- name
- } = this.state
-
- const { style, defaultValue } = this.props
-
- return (
-
- )
- }
-
- updateSrc = () => {
- const {
- name,
- namespace,
- new_value: newValue,
- existing_value: existingValue,
- updated_src: updatedSrc,
- type
- } = ObjectAttributes.get(this.rjvId, 'action', 'variable-update')
- const { onEdit, onDelete, onAdd } = this.props
-
- const { src } = this.state
-
- let result
-
- const onEditPayload = {
- existing_src: src,
- new_value: newValue,
- updated_src: updatedSrc,
- name,
- namespace,
- existing_value: existingValue
- }
-
- switch (type) {
- case 'variable-added':
- result = onAdd(onEditPayload)
- break
- case 'variable-edited':
- result = onEdit(onEditPayload)
- break
- case 'variable-removed':
- result = onDelete(onEditPayload)
- break
- }
-
- if (result !== false) {
- ObjectAttributes.set(this.rjvId, 'global', 'src', updatedSrc)
- this.setState({
- src: updatedSrc
- })
- } else {
- this.setState({
- validationFailure: true
- })
- }
- }
-
- addKeyRequest = () => {
- this.setState({
- addKeyRequest: true
- })
- }
+export default ReactJsonView
- resetState = () => {
- this.setState({
- validationFailure: false,
- addKeyRequest: false
- })
- }
+export {
+ ReactJsonView,
+ ReactPureJsonView,
+ defaultBigNumberImplement,
+ defaultJSONImplement
}
-
-polyfill(ReactJsonView)
-
-export default ReactJsonView
diff --git a/test/tests/js/Index-jsonstring-test.js b/test/tests/js/Index-jsonstring-test.js
new file mode 100644
index 0000000..7413193
--- /dev/null
+++ b/test/tests/js/Index-jsonstring-test.js
@@ -0,0 +1,52 @@
+import React from 'react'
+import { render } from 'enzyme'
+import { expect } from 'chai'
+import { JSDOM } from 'jsdom'
+
+import { ReactPureJsonView as Index} from './../../../src/js/index'
+
+const { window } = new JSDOM()
+global.window = window
+global.document = window.document
+
+describe('', function () {
+ it('index can have ArrayGroup root component', function () {
+ const wrapper = render(
+
+ )
+ expect(wrapper.find('.array-group')).to.have.length(3)
+ })
+
+ it('length is correct even if an object has a length property', function () {
+ const wrapper = render(
+
+ )
+ expect(wrapper.find('.object-size')).to.have.length(1)
+ })
+
+ it('bigint field is correct parsed with object\'s json string', function () {
+ const wrapper = render(
+
+ )
+
+ expect(wrapper.find('.object-size')).to.have.length(1)
+ })
+
+ it('bigint field is correct parsed with array\'s json string', function () {
+ const wrapper = render(
+
+ )
+
+ expect(wrapper.find('.object-size')).to.have.length(1)
+ })
+})