Skip to content

Commit 7982300

Browse files
committed
feat: support default inspect state with defaultInspectControl
1 parent c9a6240 commit 7982300

File tree

7 files changed

+62
-20
lines changed

7 files changed

+62
-20
lines changed

docs/pages/apis.mdx

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
| `enableClipboard` | `boolean` | `true` | Whether enable clipboard feature. |
1818
| `editable` | `boolean` \|<br />`(path, currentValue) => boolean` | `false` | Whether enable edit feature. Provide a function to customize this behavior by returning a boolean based on the value and path. |
1919
| `defaultInspectDepth` | `number` | 5 | Default inspect depth for nested objects.<br /><br />_\* If the number is set too large, it could result in performance issues._ |
20+
| `defaultInspectControl` | `(path, currentValue) => boolean` | - | Whether expand or collapse a field by default. Using this will override `defaultInspectDepth`. |
2021
| `maxDisplayLength` | `number` | 30 | Hide items after reaching the count.<br />`Array` and `Object` will be affected.<br /><br />_\* If the number is set too large, it could result in performance issues._ |
2122
| `groupArraysAfterLength` | `number` | 100 | Group arrays after reaching the count.<br />Groups are displayed with bracket notation and can be expanded and collapsed by clicking on the brackets. |
2223
| `collapseStringsAfterLength` | `number` | 50 | Cut off the string after reaching the count. Collapsed strings are followed by an ellipsis.<br /><br />String content can be expanded and collapsed by clicking on the string value. |

src/components/DataTypes/Object.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,10 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
203203
const prevElements = Array.isArray(props.prevValue) ? segmentArray(props.prevValue, groupArraysAfterLength) : undefined
204204

205205
return elements.map((list, index) => {
206-
const path = [...props.path]
207206
return (
208207
<DataKeyPair
209208
key={index}
210-
path={path}
209+
path={props.path}
211210
value={list}
212211
nestedIndex={index}
213212
prevValue={prevElements?.[index]}

src/hooks/useInspect.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ import {
88
useState
99
} from 'react'
1010

11-
import {
12-
useJsonViewerStore
13-
} from '../stores/JsonViewerStore'
11+
import { useJsonViewerStore } from '../stores/JsonViewerStore'
1412
import { useIsCycleReference } from './useIsCycleReference'
1513

1614
export function useInspect (path: (string | number)[], value: any, nestedIndex?: number) {
@@ -19,21 +17,28 @@ export function useInspect (path: (string | number)[], value: any, nestedIndex?:
1917
const getInspectCache = useJsonViewerStore(store => store.getInspectCache)
2018
const setInspectCache = useJsonViewerStore(store => store.setInspectCache)
2119
const defaultInspectDepth = useJsonViewerStore(store => store.defaultInspectDepth)
20+
const defaultInspectControl = useJsonViewerStore(store => store.defaultInspectControl)
2221
useEffect(() => {
2322
const inspect = getInspectCache(path, nestedIndex)
2423
if (inspect !== undefined) {
2524
return
2625
}
26+
27+
// item with nestedIndex should not be inspected
2728
if (nestedIndex !== undefined) {
2829
setInspectCache(path, false, nestedIndex)
29-
} else {
30-
// do not inspect when it is a cycle reference, otherwise there will have a loop
31-
const inspect = isTrap
32-
? false
33-
: depth < defaultInspectDepth
34-
setInspectCache(path, inspect)
30+
return
3531
}
36-
}, [defaultInspectDepth, depth, getInspectCache, isTrap, nestedIndex, path, setInspectCache])
32+
33+
// do not inspect when it is a cycle reference, otherwise there will have a loop
34+
const shouldInspect = isTrap
35+
? false
36+
: typeof defaultInspectControl === 'function'
37+
? defaultInspectControl(path, value)
38+
: depth < defaultInspectDepth
39+
setInspectCache(path, shouldInspect)
40+
}, [defaultInspectDepth, defaultInspectControl, depth, getInspectCache, isTrap, nestedIndex, path, value, setInspectCache])
41+
3742
const [inspect, set] = useState<boolean>(() => {
3843
const shouldInspect = getInspectCache(path, nestedIndex)
3944
if (shouldInspect !== undefined) {
@@ -44,7 +49,9 @@ export function useInspect (path: (string | number)[], value: any, nestedIndex?:
4449
}
4550
return isTrap
4651
? false
47-
: depth < defaultInspectDepth
52+
: typeof defaultInspectControl === 'function'
53+
? defaultInspectControl(path, value)
54+
: depth < defaultInspectDepth
4855
})
4956
const setInspect = useCallback<Dispatch<SetStateAction<boolean>>>((apply) => {
5057
set((oldState) => {

src/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ const JsonViewerInner: FC<JsonViewerProps> = (props) => {
100100

101101
const value = useJsonViewerStore(store => store.value)
102102
const prevValue = useJsonViewerStore(store => store.prevValue)
103+
const emptyPath = useMemo(() => [], [])
103104
const setHover = useJsonViewerStore(store => store.setHover)
104105
const onMouseLeave = useCallback(() => setHover(null), [setHover])
105106
return (
@@ -118,7 +119,7 @@ const JsonViewerInner: FC<JsonViewerProps> = (props) => {
118119
<DataKeyPair
119120
value={value}
120121
prevValue={prevValue}
121-
path={useMemo(() => [], [])}
122+
path={emptyPath}
122123
/>
123124
</Paper>
124125
)

src/stores/JsonViewerStore.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type JsonViewerState<T = unknown> = {
2626
highlightUpdates: boolean
2727
maxDisplayLength: number
2828
defaultInspectDepth: number
29+
defaultInspectControl?: (path: Path, value: unknown) => boolean
2930
collapseStringsAfterLength: number
3031
objectSortKeys: boolean | ((a: string, b: string) => number)
3132
quotesOnKeys: boolean
@@ -42,8 +43,7 @@ export type JsonViewerState<T = unknown> = {
4243
displaySize: boolean | ((path: Path, value: unknown) => boolean)
4344

4445
getInspectCache: (path: Path, nestedIndex?: number) => boolean
45-
setInspectCache: (
46-
path: Path, action: SetStateAction<boolean>, nestedIndex?: number) => void
46+
setInspectCache: (path: Path, action: SetStateAction<boolean>, nestedIndex?: number) => void
4747
setHover: (path: Path | null, nestedIndex?: number) => void
4848
}
4949

@@ -66,6 +66,7 @@ export const createJsonViewerStore = <T = unknown> (props: JsonViewerProps<T>) =
6666
keyRenderer: props.keyRenderer ?? DefaultKeyRenderer,
6767
editable: props.editable ?? false,
6868
defaultInspectDepth: props.defaultInspectDepth ?? 5,
69+
defaultInspectControl: props.defaultInspectControl ?? undefined,
6970
objectSortKeys: props.objectSortKeys ?? false,
7071
quotesOnKeys: props.quotesOnKeys ?? true,
7172
displayDataTypes: props.displayDataTypes ?? true,
@@ -79,15 +80,13 @@ export const createJsonViewerStore = <T = unknown> (props: JsonViewerProps<T>) =
7980

8081
getInspectCache: (path, nestedIndex) => {
8182
const target = nestedIndex !== undefined
82-
? path.join('.') +
83-
`[${nestedIndex}]nt`
83+
? path.join('.') + `[${nestedIndex}]nt`
8484
: path.join('.')
8585
return get().inspectCache[target]
8686
},
8787
setInspectCache: (path, action, nestedIndex) => {
8888
const target = nestedIndex !== undefined
89-
? path.join('.') +
90-
`[${nestedIndex}]nt`
89+
? path.join('.') + `[${nestedIndex}]nt`
9190
: path.join('.')
9291
set(state => ({
9392
inspectCache: {

src/type.ts

+6
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ export type JsonViewerProps<T = unknown> = {
175175
* @default 5
176176
*/
177177
defaultInspectDepth?: number
178+
/**
179+
* Default inspect control for nested objects.
180+
*
181+
* Provide a function to customize which fields should be expanded by default.
182+
*/
183+
defaultInspectControl?: (path: Path, value: unknown) => boolean
178184
/**
179185
* Hide items after reaching the count.
180186
* `Array` and `Object` will be affected.

tests/index.test.tsx

+29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react'
22
import { expectTypeOf } from 'expect-type'
33
import { describe, expect, it } from 'vitest'
44

5+
import type { Path } from '../src'
56
import { defineDataType, JsonViewer } from '../src'
67

78
function aPlusB (a: number, b: number) {
@@ -245,6 +246,34 @@ describe('render <JsonViewer/> with props', () => {
245246
})
246247
})
247248

249+
it('render with defaultInspectControl', async () => {
250+
const defaultInspectControl = (path: Path, _data: unknown) => {
251+
if (path.length === 0) return true
252+
return path.at(-1)?.toString().endsWith('On')
253+
}
254+
const data = {
255+
foo: {
256+
bar: 'bar'
257+
},
258+
fooOn: {
259+
barOn: 'barOn'
260+
}
261+
}
262+
const { container } = render(
263+
<>
264+
<JsonViewer
265+
value={data}
266+
rootName={false}
267+
displaySize={false}
268+
displayDataTypes={false}
269+
defaultInspectControl={defaultInspectControl}
270+
/>
271+
</>
272+
)
273+
expect(container.children.length).eq(1)
274+
expect(container.children.item(0)!.textContent).eq('{"foo":{…}"fooOn":{"barOn":"barOn"}}')
275+
})
276+
248277
it('render with dataTypes', async () => {
249278
render(<JsonViewer value={undefined} valueTypes={[]} />)
250279
render(

0 commit comments

Comments
 (0)