Skip to content

feat: Enhance VariableEditor to support BigNumber type handling #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}} />
```

Expand Down Expand Up @@ -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` | `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

Expand Down
17 changes: 16 additions & 1 deletion dev-server/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<div>
{/* just pass in your JSON to the src attribute */}
<JsonViewer
bigNumber={BigNumber}
sortKeys
style={{ padding: '30px', backgroundColor: 'white' }}
src={getExampleJson1()}
Expand Down Expand Up @@ -59,6 +71,7 @@ ReactDom.render(
{/* use a base16 theme */}
<JsonViewer
src={getExampleJson1()}
bigNumber={BigNumber}
theme='railscasts'
validationMessage="You're doing something wrong."
collapseStringsAfterLength={15}
Expand Down Expand Up @@ -139,6 +152,7 @@ ReactDom.render(
<JsonViewer
enableClipboard={false}
src={getExampleJson1()}
bigNumber={BigNumber}
shouldCollapse={({ src, namespace, type }) =>
namespace.indexOf('moment') > -1
}
Expand Down Expand Up @@ -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')
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/js/components/DataTypes/BigNumber.js
Original file line number Diff line number Diff line change
@@ -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 (
<div {...Theme(props.theme, 'bigNumber')}>
<DataTypeLabel type_name={type_name} {...props} />
{this.props.value.toString()}
</div>
)
}
}
1 change: 1 addition & 0 deletions src/js/components/DataTypes/DataTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
9 changes: 5 additions & 4 deletions src/js/components/DataTypes/Object.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Suntgr what's the motivation for this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variables might be a special object (like a json-bigint instance) that either doesn't inherit methods from Object.prototype, or has its hasOwnProperty method overridden/deleted.

example:

const JSONbig = require('json-bigint')  
 <JsonViewer  
      bigNumber={BigNumber}  
      src={{a: JSONbig.parse('{"c": 900719977777777999999999123}')}}  
      ...  

} else if (variable.type === 'object') {
elements.push(
<JsonObject
Expand Down Expand Up @@ -286,6 +286,7 @@ class RjvObject extends React.PureComponent {
/>
)
} else {
// include bigNumber
elements.push(
<VariableEditor
key={variable.name + '_' + namespace}
Expand All @@ -305,10 +306,10 @@ class RjvObject extends React.PureComponent {

// just store name, value and type with a variable
class JsonVariable {
constructor (name, value) {
constructor (name, value, bigNumber) {
this.name = name
this.value = value
this.type = toType(value)
this.type = toType(value, bigNumber)
}
}

Expand Down
20 changes: 14 additions & 6 deletions src/js/components/VariableEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
JsonNull,
JsonRegexp,
JsonString,
JsonUndefined
JsonUndefined,
JsonBigNumber
} from './DataTypes/DataTypes'

// clibboard icon
Expand Down Expand Up @@ -166,8 +167,8 @@ class VariableEditor extends React.PureComponent {

prepopInput = variable => {
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,
Expand Down Expand Up @@ -236,6 +237,8 @@ class VariableEditor extends React.PureComponent {
return <JsonDate value={variable.value} {...props} />
case 'regexp':
return <JsonRegexp value={variable.value} {...props} />
case 'bigNumber':
return <JsonBigNumber value={variable.value} {...props} />
default:
// catch-all for types that weren't anticipated
return <div class='object-value'>{JSON.stringify(variable.value)}</div>
Expand All @@ -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: {
Expand Down Expand Up @@ -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
})
Expand Down Expand Up @@ -456,6 +462,8 @@ class VariableEditor extends React.PureComponent {
return <JsonUndefined {...props} />
case 'date':
return <JsonDate value={new Date(value)} {...props} />
case 'bignumber':
return <JsonBigNumber value={value} {...props} />
}
}
}
Expand Down
12 changes: 10 additions & 2 deletions src/js/helpers/parseInput.js
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great

if (input[0] === '[') {
// array
return formatResponse('array', JSON.parse(input))
Expand All @@ -16,6 +16,10 @@ export default function parseInput (input) {
input.match(/\-?\d+\.\d+/) &&
input.match(/\-?\d+\.\d+/)[0] === input
) {
// big number
if (bigNumber && parseFloat(input).toString() !== input) {
return formatResponse('bigNumber', input)
}
// float
return formatResponse('float', parseFloat(input))
} else if (
Expand All @@ -25,6 +29,10 @@ export default function parseInput (input) {
// 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)
}
// integer
return formatResponse('integer', parseInt(input))
} else if (
Expand Down
7 changes: 5 additions & 2 deletions src/js/helpers/stringifyVariable.js
Original file line number Diff line number Diff line change
@@ -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': {
Expand All @@ -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
Expand Down
9 changes: 8 additions & 1 deletion src/js/helpers/util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// returns a string "type" of input object
export function toType (obj) {
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'
}
let type = getType(obj)
// some extra disambiguation for numbers
if (type === 'number') {
Expand Down
3 changes: 2 additions & 1 deletion src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion src/js/themes/getStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down