From 98d2d1e508f7121f46c2513435949a69454a75bf Mon Sep 17 00:00:00 2001 From: zhangyang8 Date: Wed, 22 Jan 2025 15:10:16 +0800 Subject: [PATCH 1/2] feat: Enhance VariableEditor to support BigNumber type handling --- README.md | 6 ++++-- src/js/components/DataTypes/BigNumber.js | 18 ++++++++++++++++++ src/js/components/DataTypes/DataTypes.js | 1 + src/js/components/DataTypes/Object.js | 9 +++++---- src/js/components/VariableEditor.js | 20 ++++++++++++++------ src/js/helpers/parseInput.js | 10 ++++++++-- src/js/helpers/stringifyVariable.js | 7 +++++-- src/js/helpers/util.js | 5 ++++- src/js/index.js | 3 ++- src/js/themes/getStyle.js | 7 ++++++- 10 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 src/js/components/DataTypes/BigNumber.js diff --git a/README.md b/README.md index 3395da4..8a6871d 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,8 @@ import ReactJsonView from '@microlink/react-json-view' 'last-child': null }, string_number: '1234', - date: new Date() + date: new Date(), + bigNumber: new BigNumber('0.0060254656709730629123') }} /> ``` @@ -81,7 +82,8 @@ import ReactJsonView from '@microlink/react-json-view' | `quotesOnKeys` | `boolean` | `true` | Set to `false` to remove quotes from keys (e.g., `"name":` vs. `name:`). | | `validationMessage` | `string` | "Validation Error" | Custom message for validation failures to `onEdit`, `onAdd`, or `onDelete` callbacks. | | `displayArrayKey` | `boolean` | `true` | When set to `true`, the index of the elements prefix values. | -| `escapeStrings` | `boolean` | `true` | When set to `true`, strings sequences such as \n, \t, \r, \f will be escaped. | +| `escapeStrings` | `boolean` | `true` | When set to `true`, strings sequences such as \n, \t, \r, \f will be escaped. | +| `bigNumber` | `BigNumber \| Decimal \| Big` | `null` | When set to a BigNumber class (from `bignumber.js`, `decimal.js` or `big.js`), numbers will be parsed and displayed using that class for high precision decimal handling. | #### Callbacks diff --git a/src/js/components/DataTypes/BigNumber.js b/src/js/components/DataTypes/BigNumber.js new file mode 100644 index 0000000..289e168 --- /dev/null +++ b/src/js/components/DataTypes/BigNumber.js @@ -0,0 +1,18 @@ +import React from 'react' +import DataTypeLabel from './DataTypeLabel' + +// theme +import Theme from './../../themes/getStyle' + +export default class extends React.PureComponent { + render () { + const type_name = 'bigNumber' + const { props } = this + return ( +
+ + {this.props.value.toString()} +
+ ) + } +} diff --git a/src/js/components/DataTypes/DataTypes.js b/src/js/components/DataTypes/DataTypes.js index 56223dd..78c8ef3 100644 --- a/src/js/components/DataTypes/DataTypes.js +++ b/src/js/components/DataTypes/DataTypes.js @@ -9,3 +9,4 @@ export { default as JsonObject } from './Object' export { default as JsonRegexp } from './Regexp' export { default as JsonString } from './String' export { default as JsonUndefined } from './Undefined' +export { default as JsonBigNumber } from './BigNumber' diff --git a/src/js/components/DataTypes/Object.js b/src/js/components/DataTypes/Object.js index d3c1b90..4d37bff 100644 --- a/src/js/components/DataTypes/Object.js +++ b/src/js/components/DataTypes/Object.js @@ -245,12 +245,12 @@ class RjvObject extends React.PureComponent { } keys.forEach(name => { - variable = new JsonVariable(name, variables[name]) + variable = new JsonVariable(name, variables[name], props.bigNumber) if (parent_type === 'array_group' && index_offset) { variable.name = parseInt(variable.name) + index_offset } - if (!variables.hasOwnProperty(name)) { + if (!Object.prototype.hasOwnProperty.call(variables, name)) { } else if (variable.type === 'object') { elements.push( ) } else { + // include bigNumber elements.push( { if (this.props.onEdit !== false) { - const stringifiedValue = stringifyVariable(variable.value) - const detected = parseInput(stringifiedValue) + const stringifiedValue = stringifyVariable(variable.value, this.props.bigNumber) + const detected = parseInput(stringifiedValue, this.props.bigNumber) this.setState({ editMode: true, editValue: stringifiedValue, @@ -236,6 +237,8 @@ class VariableEditor extends React.PureComponent { return case 'regexp': return + case 'bigNumber': + return default: // catch-all for types that weren't anticipated return
{JSON.stringify(variable.value)}
@@ -259,7 +262,7 @@ class VariableEditor extends React.PureComponent { class='variable-editor' onChange={event => { const value = event.target.value - const detected = parseInput(value) + const detected = parseInput(value, this.props.bigNumber) this.setState({ editValue: value, parsedInput: { @@ -320,12 +323,15 @@ class VariableEditor extends React.PureComponent { } submitEdit = submit_detected => { - const { variable, namespace, rjvId } = this.props + const { variable, namespace, rjvId, bigNumber: BigNumber } = this.props const { editValue, parsedInput } = this.state let new_value = editValue if (submit_detected && parsedInput.type) { new_value = parsedInput.value - } + if (BigNumber && parsedInput.type === 'bigNumber') { + new_value = new BigNumber(new_value) + } + } this.setState({ editMode: false }) @@ -456,6 +462,8 @@ class VariableEditor extends React.PureComponent { return case 'date': return + case 'bignumber': + return } } } diff --git a/src/js/helpers/parseInput.js b/src/js/helpers/parseInput.js index da0c451..5fa1e01 100644 --- a/src/js/helpers/parseInput.js +++ b/src/js/helpers/parseInput.js @@ -1,11 +1,11 @@ -export default function parseInput (input) { +export default function parseInput (input, bigNumber) { // following code is to make a best guess at // the type for a variable being submitted. // we are working with a serialized data representation input = input.trim() try { - input = JSON.stringify(JSON.parse(input)) + input = structuredClone(input) if (input[0] === '[') { // array return formatResponse('array', JSON.parse(input)) @@ -16,6 +16,9 @@ export default function parseInput (input) { input.match(/\-?\d+\.\d+/) && input.match(/\-?\d+\.\d+/)[0] === input ) { + if (bigNumber && parseFloat(input).toString() !== input) { + return formatResponse('bigNumber', input) + } // float return formatResponse('float', parseFloat(input)) } else if ( @@ -25,6 +28,9 @@ export default function parseInput (input) { // scientific float return formatResponse('float', Number(input)) } else if (input.match(/\-?\d+/) && input.match(/\-?\d+/)[0] === input) { + if (bigNumber && parseInt(input).toString() !== input) { + return formatResponse('bigNumber', input) + } // integer return formatResponse('integer', parseInt(input)) } else if ( diff --git a/src/js/helpers/stringifyVariable.js b/src/js/helpers/stringifyVariable.js index 4d912b2..ad4275b 100644 --- a/src/js/helpers/stringifyVariable.js +++ b/src/js/helpers/stringifyVariable.js @@ -1,7 +1,7 @@ import { toType } from './util' -export default value => { - const type = toType(value) +export default (value, bigNumber) => { + const type = toType(value, bigNumber) let string_value switch (type) { case 'undefined': { @@ -14,6 +14,9 @@ export default value => { case 'string': string_value = value break + case 'bigNumber': + string_value = value.toString() + break case 'date': string_value = value.toString() break diff --git a/src/js/helpers/util.js b/src/js/helpers/util.js index 6654f8a..de92cc5 100644 --- a/src/js/helpers/util.js +++ b/src/js/helpers/util.js @@ -1,5 +1,8 @@ // returns a string "type" of input object -export function toType (obj) { +export function toType (obj, bigNumber) { + if (bigNumber && obj?.constructor?.name === bigNumber?.name) { + return 'bigNumber' + } let type = getType(obj) // some extra disambiguation for numbers if (type === 'number') { diff --git a/src/js/index.js b/src/js/index.js index 0819e00..0e48d17 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -59,7 +59,8 @@ class ReactJsonView extends React.PureComponent { defaultValue: null, displayArrayKey: true, selectOnFocus: false, - keyModifier: e => e.metaKey || e.ctrlKey + keyModifier: e => e.metaKey || e.ctrlKey, + bigNumber: null } // will trigger whenever setState() is called, or parent passes in new props. diff --git a/src/js/themes/getStyle.js b/src/js/themes/getStyle.js index b05affd..a8b290d 100644 --- a/src/js/themes/getStyle.js +++ b/src/js/themes/getStyle.js @@ -25,7 +25,8 @@ const colorMap = theme => ({ null: theme.base0A, undefined: theme.base05, regexp: theme.base0A, - background: theme.base02 + background: theme.base02, + bigNumber: theme.base09 }, editVariable: { editIcon: theme.base0E, @@ -182,6 +183,10 @@ const getDefaultThemeStyling = theme => { display: 'inline-block', color: colors.dataTypes.integer }, + bigNumber: { + display: 'inline-block', + color: colors.dataTypes.bigNumber + }, string: { display: 'inline-block', color: colors.dataTypes.string From 86c14cee21e53e4ea9504e2ff828fff142251b77 Mon Sep 17 00:00:00 2001 From: zhangyang8 Date: Thu, 23 Jan 2025 17:53:14 +0800 Subject: [PATCH 2/2] docs: Update README and Demo for BigNumber --- README.md | 2 +- dev-server/src/index.js | 17 ++++++++++++++++- src/js/helpers/parseInput.js | 2 ++ src/js/helpers/util.js | 4 ++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a6871d..ddbeb3e 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ import ReactJsonView from '@microlink/react-json-view' | `validationMessage` | `string` | "Validation Error" | Custom message for validation failures to `onEdit`, `onAdd`, or `onDelete` callbacks. | | `displayArrayKey` | `boolean` | `true` | When set to `true`, the index of the elements prefix values. | | `escapeStrings` | `boolean` | `true` | When set to `true`, strings sequences such as \n, \t, \r, \f will be escaped. | -| `bigNumber` | `BigNumber \| Decimal \| Big` | `null` | When set to a BigNumber class (from `bignumber.js`, `decimal.js` or `big.js`), numbers will be parsed and displayed using that class for high precision decimal handling. | +| `bigNumber` | `Class` | `null` | A custom class for handling large numbers. The class should have a constructor that accepts a numeric string/value and a `name` property for display purposes. You can use existing libraries like `bignumber.js`, `decimal.js`, `big.js`, or provide your own implementation. | #### Callbacks diff --git a/dev-server/src/index.js b/dev-server/src/index.js index 9158e1f..229ae0e 100644 --- a/dev-server/src/index.js +++ b/dev-server/src/index.js @@ -9,11 +9,23 @@ import Moment from 'moment' //import the react-json-view component (installed with npm) import JsonViewer 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 */} namespace.indexOf('moment') > -1 } @@ -220,7 +234,8 @@ function getExampleJson1 () { string_number: '1234', date: new Date(), moment: Moment(), - regexp: /[0-9]/gi + regexp: /[0-9]/gi, + bigNumber: new BigNumber('0.0060254656709730629123') } } diff --git a/src/js/helpers/parseInput.js b/src/js/helpers/parseInput.js index 5fa1e01..39aaca5 100644 --- a/src/js/helpers/parseInput.js +++ b/src/js/helpers/parseInput.js @@ -16,6 +16,7 @@ export default function parseInput (input, bigNumber) { input.match(/\-?\d+\.\d+/) && input.match(/\-?\d+\.\d+/)[0] === input ) { + // big number if (bigNumber && parseFloat(input).toString() !== input) { return formatResponse('bigNumber', input) } @@ -28,6 +29,7 @@ export default function parseInput (input, bigNumber) { // scientific float return formatResponse('float', Number(input)) } else if (input.match(/\-?\d+/) && input.match(/\-?\d+/)[0] === input) { + // big number if (bigNumber && parseInt(input).toString() !== input) { return formatResponse('bigNumber', input) } diff --git a/src/js/helpers/util.js b/src/js/helpers/util.js index de92cc5..2ec1388 100644 --- a/src/js/helpers/util.js +++ b/src/js/helpers/util.js @@ -1,5 +1,9 @@ // returns a string "type" of input object export function toType (obj, bigNumber) { + + /* Check if the object is an instance of the custom BigNumber class passed in as a prop + * If it matches, return 'bigNumber' type so it can be displayed appropriately + */ if (bigNumber && obj?.constructor?.name === bigNumber?.name) { return 'bigNumber' }