Skip to content

Commit 5af0122

Browse files
authored
feat: collapse all empty iterables and disable expanding them (#123)
1 parent a2f2145 commit 5af0122

File tree

6 files changed

+235
-46
lines changed

6 files changed

+235
-46
lines changed

docs/pages/full/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@ const example = {
6767
string: 'this is a string',
6868
integer: 42,
6969
array: [19, 19, 810, 'test', NaN],
70+
emptyArray: [],
7071
nestedArray: [
7172
[1, 2],
7273
[3, 4]
7374
],
7475
map,
76+
emptyMap: new Map(),
7577
set,
78+
emptySet: new Set(),
7679
float: 114.514,
7780
undefined,
7881
superLongString,
@@ -81,6 +84,7 @@ const example = {
8184
'second-child': false,
8285
'last-child': null
8386
},
87+
emptyObject: {},
8488
function: aPlusB,
8589
constFunction: aPlusBConst,
8690
anonymousFunction: function (a: number, b: number) {

src/components/DataKeyPair.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useInspect } from '../hooks/useInspect'
1616
import { useJsonViewerStore } from '../stores/JsonViewerStore'
1717
import { useTypeComponents } from '../stores/typeRegistry'
1818
import type { DataItemProps } from '../type'
19+
import { getValueSize } from '../utils'
1920
import { DataBox } from './mui/DataBox'
2021

2122
export type DataKeyPairProps = {
@@ -62,10 +63,8 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
6263
const [editing, setEditing] = useState(false)
6364
const onChange = useJsonViewerStore(store => store.onChange)
6465
const keyColor = useTextColor()
65-
const numberKeyColor = useJsonViewerStore(
66-
store => store.colorspace.base0C)
67-
const { Component, PreComponent, PostComponent, Editor } = useTypeComponents(
68-
value, path)
66+
const numberKeyColor = useJsonViewerStore(store => store.colorspace.base0C)
67+
const { Component, PreComponent, PostComponent, Editor } = useTypeComponents(value, path)
6968
const quotesOnKeys = useJsonViewerStore(store => store.quotesOnKeys)
7069
const rootName = useJsonViewerStore(store => store.rootName)
7170
const isRoot = root === value
@@ -176,7 +175,8 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
176175
value
177176
])
178177

179-
const expandable = !!(PreComponent && PostComponent)
178+
const isEmptyValue = useMemo(() => getValueSize(value) === 0, [value])
179+
const expandable = !isEmptyValue && !!(PreComponent && PostComponent)
180180
const KeyRenderer = useJsonViewerStore(store => store.keyRenderer)
181181
const downstreamProps: DataItemProps = useMemo(() => ({
182182
path,
@@ -206,7 +206,9 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
206206
if (event.isDefaultPrevented()) {
207207
return
208208
}
209-
setInspect(state => !state)
209+
if (!isEmptyValue) {
210+
setInspect(state => !state)
211+
}
210212
}, [setInspect])
211213
}
212214
>

src/components/DataTypes/Object.tsx

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useTextColor } from '../../hooks/useColor'
66
import { useIsCycleReference } from '../../hooks/useIsCycleReference'
77
import { useJsonViewerStore } from '../../stores/JsonViewerStore'
88
import type { DataItemProps } from '../../type'
9+
import { getValueSize } from '../../utils'
910
import { DataKeyPair } from '../DataKeyPair'
1011
import { CircularArrowsIcon } from '../icons/CircularArrowsIcon'
1112
import { DataBox } from '../mui/DataBox'
@@ -16,15 +17,11 @@ const objectRb = '}'
1617
const arrayRb = ']'
1718

1819
function inspectMetadata (value: object) {
19-
let length
20+
const length = getValueSize(value)
21+
2022
let name = ''
21-
if (Array.isArray(value)) {
22-
length = value.length
23-
} else if (value instanceof Map || value instanceof Set) {
23+
if (value instanceof Map || value instanceof Set) {
2424
name = value[Symbol.toStringTag]
25-
length = value.size
26-
} else {
27-
length = Object.keys(value).length
2825
}
2926
if (Object.prototype.hasOwnProperty.call(value, Symbol.toStringTag)) {
3027
name = (value as any)[Symbol.toStringTag]
@@ -36,9 +33,8 @@ export const PreObjectType: React.FC<DataItemProps<object>> = (props) => {
3633
const metadataColor = useJsonViewerStore(store => store.colorspace.base04)
3734
const textColor = useTextColor()
3835
const isArray = useMemo(() => Array.isArray(props.value), [props.value])
39-
const sizeOfValue = useMemo(
40-
() => props.inspect ? inspectMetadata(props.value) : '',
41-
[props.inspect, props.value]
36+
const isEmptyValue = useMemo(() => getValueSize(props.value) === 0, [props.value])
37+
const sizeOfValue = useMemo(() => inspectMetadata(props.value), [props.inspect, props.value]
4238
)
4339
const displayObjectSize = useJsonViewerStore(store => store.displayObjectSize)
4440
const isTrap = useIsCycleReference(props.path, props.value)
@@ -50,20 +46,18 @@ export const PreObjectType: React.FC<DataItemProps<object>> = (props) => {
5046
}}
5147
>
5248
{isArray ? arrayLb : objectLb}
53-
{displayObjectSize
54-
? (
55-
<Box
56-
component='span'
57-
sx={{
58-
pl: 0.5,
59-
fontStyle: 'italic',
60-
color: metadataColor
61-
}}
62-
>
63-
{sizeOfValue}
64-
</Box>
65-
)
66-
: null}
49+
{displayObjectSize && props.inspect && !isEmptyValue && (
50+
<Box
51+
component='span'
52+
sx={{
53+
pl: 0.5,
54+
fontStyle: 'italic',
55+
color: metadataColor
56+
}}
57+
>
58+
{sizeOfValue}
59+
</Box>
60+
)}
6761

6862
{isTrap && !props.inspect
6963
? (
@@ -85,14 +79,13 @@ export const PostObjectType: React.FC<DataItemProps<object>> = (props) => {
8579
const metadataColor = useJsonViewerStore(store => store.colorspace.base04)
8680
const isArray = useMemo(() => Array.isArray(props.value), [props.value])
8781
const displayObjectSize = useJsonViewerStore(store => store.displayObjectSize)
88-
const sizeOfValue = useMemo(
89-
() => !props.inspect ? inspectMetadata(props.value) : '',
90-
[props.inspect, props.value]
91-
)
82+
const isEmptyValue = useMemo(() => getValueSize(props.value) === 0, [props.value])
83+
const sizeOfValue = useMemo(() => inspectMetadata(props.value), [props.inspect, props.value])
84+
9285
return (
9386
<Box component='span' className='data-object-end'>
9487
{isArray ? arrayRb : objectRb}
95-
{displayObjectSize
88+
{displayObjectSize && (isEmptyValue || !props.inspect)
9689
? (
9790
<Box
9891
component='span'
@@ -118,12 +111,9 @@ function getIterator (value: any): value is Iterable<unknown> {
118111
export const ObjectType: React.FC<DataItemProps<object>> = (props) => {
119112
const keyColor = useTextColor()
120113
const borderColor = useJsonViewerStore(store => store.colorspace.base02)
121-
const groupArraysAfterLength = useJsonViewerStore(
122-
store => store.groupArraysAfterLength)
114+
const groupArraysAfterLength = useJsonViewerStore(store => store.groupArraysAfterLength)
123115
const isTrap = useIsCycleReference(props.path, props.value)
124-
const [displayLength, setDisplayLength] = useState(
125-
useJsonViewerStore(store => store.maxDisplayLength)
126-
)
116+
const [displayLength, setDisplayLength] = useState(useJsonViewerStore(store => store.maxDisplayLength))
127117
const objectSortKeys = useJsonViewerStore(store => store.objectSortKeys)
128118
const elements = useMemo(() => {
129119
if (!props.inspect) {
@@ -201,10 +191,9 @@ export const ObjectType: React.FC<DataItemProps<object>> = (props) => {
201191
// object
202192
let entries: [key: string, value: unknown][] = Object.entries(value)
203193
if (objectSortKeys) {
204-
entries = entries.sort(([a], [b]) => objectSortKeys === true
205-
? a.localeCompare(b)
206-
: objectSortKeys(a, b)
207-
)
194+
entries = objectSortKeys === true
195+
? entries.sort(([a], [b]) => a.localeCompare(b))
196+
: entries.sort(([a], [b]) => objectSortKeys(a, b))
208197
}
209198
const elements = entries.slice(0, displayLength).map(([key, value]) => {
210199
const path = [...props.path, key]
@@ -243,6 +232,10 @@ export const ObjectType: React.FC<DataItemProps<object>> = (props) => {
243232
const marginLeft = props.inspect ? 0.6 : 0
244233
const width = useJsonViewerStore(store => store.indentWidth)
245234
const indentWidth = props.inspect ? width - marginLeft : width
235+
const isEmptyValue = useMemo(() => getValueSize(props.value) === 0, [props.value])
236+
if (isEmptyValue) {
237+
return null
238+
}
246239
return (
247240
<Box
248241
className='data-object'

src/hooks/useInspect.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ export function useInspect (path: (string | number)[], value: any, nestedIndex?:
1616
const isTrap = useIsCycleReference(path, value)
1717
const getInspectCache = useJsonViewerStore(store => store.getInspectCache)
1818
const setInspectCache = useJsonViewerStore(store => store.setInspectCache)
19-
const defaultInspectDepth = useJsonViewerStore(
20-
store => store.defaultInspectDepth)
19+
const defaultInspectDepth = useJsonViewerStore(store => store.defaultInspectDepth)
2120
useEffect(() => {
2221
const inspect = getInspectCache(path, nestedIndex)
2322
if (inspect !== undefined) {

src/utils/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,20 @@ export const isCycleReference = (
123123
}
124124
return false
125125
}
126+
127+
export function getValueSize (value: any): number {
128+
if (value === null || undefined) {
129+
return 0
130+
} else if (Array.isArray(value)) {
131+
return value.length
132+
} else if (value instanceof Map || value instanceof Set) {
133+
return value.size
134+
} else if (value instanceof Date) {
135+
return 1
136+
} else if (typeof value === 'object') {
137+
return Object.keys(value).length
138+
} else if (typeof value === 'string') {
139+
return value.length
140+
}
141+
return 1
142+
}

0 commit comments

Comments
 (0)