Skip to content

Add custom buttons to rows according to value type #79

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [1.26.3](https://github.com/n1c0de/react-json-view/compare/v1.26.2...v1.26.3) (2025-05-26)

### [1.26.2](https://github.com/microlinkhq/react-json-view/compare/v1.26.1...v1.26.2) (2025-05-16)

### [1.26.1](https://github.com/microlinkhq/react-json-view/compare/v1.26.0...v1.26.1) (2025-02-15)
Expand Down
200 changes: 199 additions & 1 deletion dev-server/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ ReactDom.render(

<br />

{/*demo array support*/}
{/* demo array support */}
<JsonViewer
src={getExampleArray()}
theme='solarized'
Expand Down Expand Up @@ -201,6 +201,172 @@ ReactDom.render(
name='String with special escape sequences'
src={getExampleWithStringEscapeSequences()}
/>

{/* Custom buttons according to the value type */}
<JsonViewer
bigNumber={BigNumber}
sortKeys
style={{ padding: '30px', backgroundColor: 'white' }}
src={getExampleJson5()}
quotesOnKeys={false}
collapseStringsAfterLength={12}
customButtons={{
boolean: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
integer: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: (element) => element.src === 27
? <path d='M0 14l6-6-6-6z' />
: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: (element) => element.src === 27
? '0 0 15 15'
: '0 0 40 40',
title: (element) => element.src === 27
? 'Special title'
: 'Example title',
className: (element) => element.src === 27
? 'special-class'
: 'class-example'
},
float: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
bigNumber: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
date: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
string: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: (element) => element.variableName === 'string-key-test'
? <path d='M1344 800v64q0 14-9 23t-23 9h-352v352q0 14-9 23t-23 9h-64q-14 0-23-9t-9-23v-352h-352q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h352v-352q0-14 9-23t23-9h64q14 0 23 9t9 23v352h352q14 0 23 9t9 23zm128 448v-832q0-66-47-113t-113-47h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z' />
: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: (element) => element.variableName === 'string-key-test'
?'0 0 1792 1792'
:'0 0 40 40',
title: (element) => element.variableName === 'string-key-test'
? 'Special title'
: 'Title example',
className: (element) => element.variableName === 'string-key-test'
? 'special-class'
: 'class-example'
},
regexp: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
array: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
empty_array: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
object: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
empty_object: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
function: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
undefined: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
null: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
nan: {
clickCallback: (element) => { console.log(JSON.stringify(element, null, 4)) },
path: <path d="m31.7 16.4q0-0.6-0.4-1l-2.1-2.1q-0.4-0.4-1-0.4t-1 0.4l-9.1 9.1-5-5q-0.5-0.4-1-0.4t-1 0.4l-2.1 2q-0.4 0.4-0.4 1 0 0.6 0.4 1l8.1 8.1q0.4 0.4 1 0.4 0.6 0 1-0.4l12.2-12.1q0.4-0.4 0.4-1z m5.6 3.6q0 4.7-2.3 8.6t-6.3 6.2-8.6 2.3-8.6-2.3-6.2-6.2-2.3-8.6 2.3-8.6 6.2-6.2 8.6-2.3 8.6 2.3 6.3 6.2 2.3 8.6z" />,
viewBox: '0 0 40 40',
title: 'A title example',
className: 'class-example'
},
}}
onEdit={e => {
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={'custom-buttons'}
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=''
/>
</div>,
document.getElementById('app-container')
)
Expand Down Expand Up @@ -320,3 +486,35 @@ function getExampleArray () {
function getExampleWithStringEscapeSequences () {
return { '\\\n\t\r\f\\n': '\\\n\t\r\f\\n' }
}

function getExampleJson5 () {
return {
string: 'this is a test string',
'string-key-test': 'this is another test string',
integer: 42,
'integer-key-test': 27,
empty_array: [],
empty_object: {},
array: [1, 2, 3, 'test'],
float: -2.757,
undefined_var: undefined,
parent: {
sibling1: true,
sibling2: false,
sibling3: null,
sibling4: NaN,
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'),
}
}
54 changes: 27 additions & 27 deletions docs/src/js/components/Demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ class Demo extends React.PureComponent {
`}
</style>
)}
<div class='rjv-demo'>
<div class='rjv-header'>
<div class='header-1'>@microlink/react-json-view</div>
<div className='rjv-demo'>
<div className='rjv-header'>
<div className='header-1'>@microlink/react-json-view</div>
</div>
<ReactJson
name={false}
Expand Down Expand Up @@ -217,52 +217,52 @@ class Demo extends React.PureComponent {
iconStyle={iconStyle}
/>

<div class='rjv-settings'>
<div class='rjv-input'>
<div class='rjv-label'>Theme:</div>
<div className='rjv-settings'>
<div className='rjv-input'>
<div className='rjv-label'>Theme:</div>
{this.getThemeInput(theme)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Icon Style:</div>
<div className='rjv-input'>
<div className='rjv-label'>Icon Style:</div>
{this.getIconStyleInput(iconStyle)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Enable Edit:</div>
<div className='rjv-input'>
<div className='rjv-label'>Enable Edit:</div>
{this.getEditInput(onEdit)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Enable Add:</div>
<div className='rjv-input'>
<div className='rjv-label'>Enable Add:</div>
{this.getAddInput(onAdd)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Enable Delete:</div>
<div className='rjv-input'>
<div className='rjv-label'>Enable Delete:</div>
{this.getDeleteInput(onDelete)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Enable Clipboard:</div>
<div className='rjv-input'>
<div className='rjv-label'>Enable Clipboard:</div>
{this.getEnableClipboardInput(enableClipboard)}
</div>
</div>

<div class='rjv-settings'>
<div class='rjv-input'>
<div class='rjv-label'>Display Data Types:</div>
<div className='rjv-settings'>
<div className='rjv-input'>
<div className='rjv-label'>Display Data Types:</div>
{this.getDataTypesInput(displayDataTypes)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Display Object Size:</div>
<div className='rjv-input'>
<div className='rjv-label'>Display Object Size:</div>
{this.getObjectSizeInput(displayObjectSize)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Indent Width:</div>
<div className='rjv-input'>
<div className='rjv-label'>Indent Width:</div>
{this.getIndentWidthInput(indentWidth)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Collapsed:</div>
<div className='rjv-input'>
<div className='rjv-label'>Collapsed:</div>
{this.getCollapsedInput(collapsed)}
</div>
<div class='rjv-input'>
<div class='rjv-label'>Collapse Strings After Length:</div>
<div className='rjv-input'>
<div className='rjv-label'>Collapse Strings After Length:</div>
{this.getCollapsedStringsInput(collapseStringsAfter)}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/src/js/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const app = document.getElementById('mac-react-container')

//app entrypoint
ReactDom.render(
<div class='app-entry'>
<div className='app-entry'>
<Index />
</div>,
app
Expand Down
2 changes: 1 addition & 1 deletion docs/src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ReactJsonDemo from './components/Demo'
// index entrypoint component
export default function Demo () {
return (
<div class='mac-react'>
<div className='mac-react'>
<ReactJsonDemo />
</div>
)
Expand Down
16 changes: 16 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ export interface ReactJsonViewProps {
* Default: true
*/
escapeStrings?: boolean
/**
* Adds custom buttons according to the value type.
*
* Default: null
*/
customButtons?: TypeCustomButtons
}

export interface OnCopyProps {
Expand Down Expand Up @@ -308,5 +314,15 @@ export type ThemeKeys =
| 'tube'
| 'twilight'

export type TypeCustomButtons = {
[valueType: string]: {
clickCallback: (element: { variableName: string; src: string; namespace: Array<string>; name: string; }) => void
path: React.ReactElement<React.SVGProps<SVGPathElement>> | ((element: { variableName: string; src: string; namespace: Array<string>; name: string; }) => React.ReactElement<React.SVGProps<SVGPathElement>>)
viewBox?: string | ((element: { variableName: string; src: string; namespace: Array<string>; name: string; }) => string)
title?: string | ((element: { variableName: string; src: string; namespace: Array<string>; name: string; }) => string)
className?: string | ((element: { variableName: string; src: string; namespace: Array<string>; name: string; }) => string)
}
}

declare const ReactJson: React.ComponentType<ReactJsonViewProps>
export default ReactJson
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@microlink/react-json-view",
"description": "Interactive react component for displaying javascript arrays and JSON objects.",
"homepage": "https://github.com/microlinkhq/react-json-view",
"version": "1.26.2",
"version": "1.26.3",
Copy link
Member

Choose a reason for hiding this comment

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

The version will incremented before merging the PR.

Pleae don't increment the version in the PR!

Copy link
Contributor Author

@n1c0de n1c0de May 27, 2025

Choose a reason for hiding this comment

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

You're right, it comes from the git flow I use in my fork. I will solve it.

"main": "dist/main.js",
"author": {
"name": "Mac Gainor"
Expand Down Expand Up @@ -175,6 +175,10 @@
{
"name": "Mert Donmezyurek",
"email": "[email protected]"
},
{
"name": "n1c0de",
"email": "[email protected]"
}
],
"repository": {
Expand Down
Loading
Loading