@@ -10,6 +10,10 @@ interface JsonProps {
1010 * JSON viewer component with collapsible objects and arrays.
1111 */
1212export default function Json ( { json, label } : JsonProps ) : ReactNode {
13+ return < div className = { styles . json } > < JsonContent json = { json } label = { label } /> </ div >
14+ }
15+
16+ function JsonContent ( { json, label } : JsonProps ) : ReactNode {
1317 let div
1418 if ( Array . isArray ( json ) ) {
1519 div = < JsonArray array = { json } label = { label } />
@@ -29,17 +33,67 @@ export default function Json({ json, label }: JsonProps): ReactNode {
2933 div = < > { key } < span > { JSON . stringify ( json ) } </ span > </ >
3034 }
3135 }
32- return < div className = { styles . json } > { div } </ div >
36+ return div
37+ }
38+
39+ function isPrimitive ( value : unknown ) : boolean {
40+ return (
41+ value !== undefined &&
42+ ! Array . isArray ( value ) &&
43+ typeof value !== 'object' &&
44+ typeof value !== 'function'
45+ )
46+ }
47+
48+ function InlineArray ( { suffix, children } : { suffix ?: string , children ?: ReactNode } ) : ReactNode {
49+ return (
50+ < >
51+ < span className = { styles . array } > { '[' } </ span >
52+ < span className = { styles . array } > { children } </ span >
53+ < span className = { styles . array } > { ']' } </ span >
54+ { suffix && < span className = { styles . comment } > { suffix } </ span > }
55+ </ >
56+ )
57+ }
58+
59+ function CollapsedArray ( { array } : { array : unknown [ ] } ) : ReactNode {
60+ const maxCharacterCount = 40
61+ const separator = ', '
62+
63+ const children : ReactNode [ ] = [ ]
64+ let suffix : string | undefined = undefined
65+
66+ let characterCount = 0
67+ for ( const [ index , item ] of array . entries ( ) ) {
68+ if ( index > 0 ) {
69+ characterCount += separator . length
70+ children . push ( < span key = { `separator-${ index - 1 } ` } > , </ span > )
71+ }
72+ // should we continue?
73+ if ( isPrimitive ( item ) ) {
74+ const asString = typeof item === 'bigint' ? item . toString ( ) : JSON . stringify ( item )
75+ characterCount += asString . length
76+ if ( characterCount < maxCharacterCount ) {
77+ children . push ( < JsonContent json = { item } key = { `item-${ index } ` } /> )
78+ continue
79+ }
80+ }
81+ // no: it was the last entry
82+ children . push ( < span key = "rest" > ...</ span > )
83+ suffix = ` length: ${ array . length } `
84+ break
85+ }
86+ return < InlineArray suffix = { suffix } > { children } </ InlineArray >
3387}
3488
3589function JsonArray ( { array, label } : { array : unknown [ ] , label ?: string } ) : ReactNode {
36- const [ collapsed , setCollapsed ] = useState ( false )
90+ const [ collapsed , setCollapsed ] = useState ( true )
3791 const key = label ? < span className = { styles . key } > { label } : </ span > : ''
3892 if ( collapsed ) {
3993 return < div className = { styles . clickable } onClick = { ( ) => { setCollapsed ( false ) } } >
4094 < span className = { styles . drill } > { '\u25B6' } </ span >
4195 { key }
42- < span className = { styles . array } > { '[...]' } </ span >
96+ < CollapsedArray array = { array } > </ CollapsedArray >
4397 </ div >
4498 }
4599 return < >
0 commit comments